Cleaning Test Runs for End to End (E2E) Tests (#12143)

* Added custom seed task

* Added a test for the initial login for the intial admin of a forem instance.

* Renamed admin login seed data file.

* Pulled functions out of main Cypress plugin file and import them now.

* Added some comments.

* Added a comment about Cypress tasks requiring a return value to be considered successful.

* Moved admin_login_setup seed file to spec folder.

* Made change requests in regards to rake task.

* Fixed reference to SiteConfig

* Created the seeder class to be shared for all seed files.

* Added a missing param to function comment.

* Fixed seeder require in e2e test seed file.

* Fixed seeder require in seeds.rb. Why is Rails.root required?

* Added an environment guard for not being production in the e2e seed task.

* Made seeder require relative.

* Trying something for the Elastic Search issue in CI.

* Revert "Trying something for the Elastic Search issue in CI."

This reverts commit 7cb2a963c8ac1f9242c612a1b9fe8ff814605df6.

* Search indices are now removed via bundle exec rake search:destroy

* Now rake search:destroy is used in the Cypress task for resetting data as well.

* Moved Seeder class to app/lib

* Added cypress-rails

* Got two flows working with db rollbacks.

* Trimmed down the e2e dataset for now.

* Added a custom Cypress command to encompass test setup.

* removed unused e2e rake task

* added some user login/password e2e tests.

* Added a note about Cypress not cleaning cookies, so we do for the moment.

* Removed code no longer required.

* Removed comment in test that is no longer pertinent to the test suite.

* Removed data cleaning plugins as cypress rails gem handles it now.

* Added @citizen428's recommendation got raising an error if trying to seed production.

* Temporarily have paralleziation of e2e tests disabled to figure out cypres-rails/knapsack pro integration.

* Doh! Forgot a semi-colon in the Travis config.

* Added a comment about not integrating cypress-rails/knapsack pro right now.

* Removed knapsack pro npm package for now as we aren't using it.

* Reworded comment about cypress-rails/kanpsackpro.

* Removed seeding for tags to follow for onboarding in e2e seed data.

* Made pree2e script e2e:setup so it only runs explicitly when needed.

* Renamed bin/e2e to bin/e2e-ci.

* Created a new script for local e2e testing.

* Removed RAILS_ENV as it's in the e2e-ci script already.

* Fixed an auto corrected command.

* Fixed wording in e2e script prompt.

* Removed bundle exec rake data_updates:run that I had added. Doesn't appear to be necessary.

* Added missing new lines.

* Renamed e2e seed file and only use that one now. THere are no others.

* Some script cleanup.

* Added a check for the E2E environment so as to not pollute system tests.

* Now e2e test server cleanup occurs when the server shuts down.

* Put puts as this is really just for testing.

* Put environment variable in the proper place for runnning bundle exec.

* Added a check to only run for E2E tests.

* Some bash script formatting.

* Removed copy paste irrelevant comments.

* Updated e2e server shutdown message.

* Now a data-testid attribute is used for finding the user account errors panel.

* Made test selectors ignore casing of text.

* Removed prod paranoia check in e2e seed file.

* Refactored spec to use new cy.loginUser(user) command.

* Update app/lib/seeder.rb

Co-authored-by: Michael Kohl <citizen428@dev.to>

* Fixed flakiness that @aitchiss was experiencing.

* Rubocop fix.

* Due to a Cypress issue, fixing cookie clearing with another tweak.

* Bumped the wait time to 500ms if cookies don't clear.

* Added a 'bundle check' to the bin/e2e script as suggested by @katiedavis.

* Removed some white space

Co-authored-by: Michael Kohl <citizen428@dev.to>
This commit is contained in:
Nick Taylor 2021-01-29 07:34:01 -05:00 committed by GitHub
parent 154b185f2d
commit a7da74b993
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 393 additions and 351 deletions

View file

@ -86,9 +86,7 @@ jobs:
- bin/test-console-check
- yarn build-storybook
- bundle exec rake data_updates:run # required for at least creating the first admin user in e2e tests.
- yarn e2e:ci
# Dropping and recreating the database for Capybara/rspecs
# - curl -XDELETE "${ELASTICSEARCH_URL}/*" # resetting elastic search post e2e tests
- bin/e2e-ci
- stage: Deploy
name: Deploy DEV

View file

@ -141,6 +141,7 @@ group :development, :test do
gem "amazing_print", "~> 1.2" # Great Ruby debugging companion: pretty print Ruby objects to visualize their structure
gem "bullet", "~> 6.1" # help to kill N+1 queries and unused eager loading
gem "capybara", "~> 3.35.1" # Capybara is an integration testing tool for rack based web applications
gem "cypress-rails", "~> 0.4.2" # For end to end tests (E2E)
gem "dotenv-rails", "~> 2.7.6" # For loading ENV variables locally
gem "faker", "~> 2.15" # A library for generating fake data such as names, addresses, and phone numbers
gem "knapsack_pro", "~> 2.11.0" # Help parallelize Ruby spec builds

View file

@ -202,6 +202,9 @@ GEM
crack (0.4.5)
rexml
crass (1.0.6)
cypress-rails (0.4.2)
puma (>= 3.8.0)
railties (>= 5.2.0)
dante (0.2.0)
database_cleaner (1.8.5)
database_cleaner-active_record (1.8.0)
@ -862,6 +865,7 @@ DEPENDENCIES
cld (~> 0.8)
cloudinary (~> 1.18)
counter_culture (~> 2.7)
cypress-rails (~> 0.4.2)
database_cleaner-active_record (~> 1.8.0)
ddtrace (~> 0.45.0)
derailed_benchmarks (~> 2.0)

47
app/lib/seeder.rb Normal file
View file

@ -0,0 +1,47 @@
# rubocop:disable Rails/Output
class Seeder
def initialize
if Rails.env.production?
puts "Can't run seeds in production"
# rubocop:disable Rails/Exit
exit 1
# rubocop:enable Rails/Exit
end
@counter = 0
end
# Used when the block is idempotent by itself and needs no further checks.
def create(message)
@counter += 1
puts " #{@counter}. #{message}."
yield
end
def create_if_none(klass, count = nil)
@counter += 1
plural = klass.name.pluralize
if klass.none?
message = ["Creating", count, plural].compact.join(" ")
puts " #{@counter}. #{message}."
yield
else
puts " #{@counter}. #{plural} already exist. Skipping."
end
end
def create_if_doesnt_exist(klass, attribute_name, attribute_value)
record = klass.find_by("#{attribute_name}": attribute_value)
if record.nil?
puts " #{klass} with #{attribute_name} = #{attribute_value} not found, proceeding..."
yield
else
puts " #{klass} with #{attribute_name} = #{attribute_value} found, skipping."
end
end
end
# rubocop:enable Rails/Output

View file

@ -9,7 +9,7 @@
If you signed up with a social media account, please <a href="<%= new_user_password_path %>">reset the password</a> for your primary email address (<%= current_user.email %>) first.
</p>
</header>
<%= form_with url: user_update_password_path do |f| %>
<%= form_with(url: user_update_password_path, data: { testid: "update-password-form" }) do |f| %>
<div class="crayons-field mb-4">
<%= f.label :current_password, class: "crayons-field__label" %>
<%= f.password_field :current_password, class: "crayons-textfield", autocomplete: "off" %>

View file

@ -1,4 +1,4 @@
<div class="crayons-card crayons-card--secondary crayons-notice crayons-notice--danger">
<div class="crayons-card crayons-card--secondary crayons-notice crayons-notice--danger" data-testid="account-errors-panel">
<div class="crayons-card__header">
<h3 class="crayons-card__headline">
<%= pluralize(errors.size, "error") %> <%= title_suffix %>:

View file

@ -2,7 +2,7 @@
<div class="crayons-card min-w-0 text-padding">
<h2 class="crayons-title mb-4"> Destroy your account</h2>
<div class="settings-form">
<%= form_tag user_full_delete_path, method: :delete, autocomplete: "off", id: "delete__account", class: 'flex flex-col gap-4 mb-4' do %>
<%= form_tag user_full_delete_path, method: :delete, autocomplete: "off", id: "delete__account", class: "flex flex-col gap-4 mb-4" do %>
Deleting your account will:
<ul class="my-0 ml-6 list-disc">
<li>delete your profile, along with your Twitter and/or GitHub associations.

24
bin/e2e
View file

@ -1,9 +1,19 @@
#!/bin/bash
if [ "$KNAPSACK_PRO_TEST_SUITE_TOKEN_CYPRESS" = "" ]; then
KNAPSACK_PRO_ENDPOINT=https://api-disabled-for-fork.knapsackpro.com \
KNAPSACK_PRO_MAX_REQUEST_RETRIES=0 \
KNAPSACK_PRO_TEST_SUITE_TOKEN_CYPRESS=disabled-for-fork \
npx cypress run # run without parallelization
else
npx knapsack-pro-cypress # parallelization is available
printf "Doing a quick bundle check to make sure gems are all up to date.\n\n"
bundle check
if [ $? -eq 1 ]; then
echo "Unable to launch end to end tests. Ensure that all your gems are installed and up to date."
exit;
fi
printf "\n"
read -p "Do you need to set up your end to end (E2E) testing database? Answer yes if this is your first time running E2E tests on your local machine or you need to recreate your E2E test database. (y/n) " -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
printf "\n\nSetting up the E2E database before running E2E tests...\n\n"
RAILS_ENV=test E2E=true bin/e2e-setup
fi
CYPRESS_RAILS_CYPRESS_OPTS="--config-file cypress.dev.json" RAILS_ENV=test E2E=true bundle exec rake cypress:open

4
bin/e2e-ci Executable file
View file

@ -0,0 +1,4 @@
#!/bin/bash
# As discussed in the engineering roundtable 2021/01/25, we will revisit cypress-rails knapsack pro integration
# for re-enabling parallelization of e2e tests later.
RAILS_ENV=test E2E=true bundle exec rake cypress:run

9
bin/e2e-setup Executable file
View file

@ -0,0 +1,9 @@
if [ -z "$E2E" ]; then
echo "Setting up the end to end (E2E) test database did not run. You are not in an E2E test environment.";
exit 1;
fi
bundle exec rails db:drop;
bundle exec rails db:create;
bundle exec rails db:schema:load;
bundle exec rails assets:precompile;

View file

@ -0,0 +1,30 @@
# We want to check not only if we are in the test environment, but also if we are
# running E2E tests. Otherwise this will run when system tests run.
return unless Rails.env.test? && ENV["E2E"].present?
# rubocop:disable Rails/Output
CypressRails.hooks.before_server_start do
# Called once, before either the transaction or the server is started
puts "Starting up server for end to end tests."
Rails.application.load_tasks
Rake::Task["db:seed:e2e"].invoke
end
CypressRails.hooks.after_transaction_start do
# Called after the transaction is started (at launch and after each reset)
end
CypressRails.hooks.after_state_reset do
# Triggered after `/cypress_rails_reset_state` is called
end
CypressRails.hooks.before_server_stop do
# Called once, at_exit
puts "Cleaning up and stopping server for end to end tests."
Rake::Task["search:destroy"].invoke
Rake::Task["db:truncate_all"].invoke
puts "The end to end test server shutdown gracefully."
end
# rubocop:enable Rails/Output

View file

@ -1,7 +1 @@
{
"baseUrl": "http://localhost:3000",
"env": {
"FOREM_OWNER_SECRET": "secret",
"ELASTICSEARCH_URL": "http://localhost:9200"
}
}
{}

View file

@ -1,3 +1,5 @@
{
"baseUrl": "http://localhost:3000"
"screenshotsFolder": "tmp/cypress_screenshots",
"trashAssetsBeforeRuns": false,
"videosFolder": "tmp/cypress_videos"
}

View file

@ -1,6 +0,0 @@
{
"name": "Admin McAdmin",
"email": "admin@forem.local",
"username": "Admin_McAdmin",
"password": "password"
}

View file

@ -0,0 +1,4 @@
{
"email": "change-password-user@forem.com",
"password": "password"
}

View file

@ -1,68 +0,0 @@
// Note if you are running these tests locallly, this test will fail
// if the first admin has already gone through the onboarding process.
describe('Initial admin signup', () => {
beforeEach(() => {
cy.task('resetData');
});
it('should sign up the initial Forem instance administrator', () => {
// This is the happy path.
cy.fixture('logins/initialAdmin.json').as('admin');
cy.log('baseUrl', Cypress.config().baseUrl);
// Go to home page which redirects to the registration form.
cy.visit('/');
cy.findByTestId('registration-form').as('registrationForm');
cy.get('@registrationForm')
.findByLabelText(/Profile image/i)
.attachFile('images/admin-image.png');
cy.get('@admin').then((admin) => {
// Enter credentials for the initial administrator user
cy.get('@registrationForm')
.findByLabelText(/^Name$/i)
.type(admin.name);
cy.get('@registrationForm')
.findByLabelText(/Username/i)
.type(admin.username);
cy.get('@registrationForm')
.findByLabelText(/^Email$/i)
.type(admin.email);
cy.get('@registrationForm')
.findByLabelText('Password')
.type(admin.password);
cy.get('@registrationForm')
.findByLabelText(/^Password Confirmation$/i)
.type(admin.password);
cy.get('@registrationForm')
.findByLabelText(/New Forem Secret/i)
.type(Cypress.env('FOREM_OWNER_SECRET'));
// Submit the form
cy.get('@registrationForm')
.findByText(/^Sign up$/)
.click();
cy.log('Submitting signup form');
// The initial administrator user was create and is redirected to the confirm email screen.
cy.url().should(
'eq',
Cypress.config().baseUrl +
'/confirm-email?email=' +
encodeURIComponent(admin.email),
);
cy.findByTestId('resend-confirmation-form').as('confirmationForm');
cy.get('@confirmationForm')
.findByLabelText(/^Confirmation email address$/i)
.should('have.value', admin.email);
cy.get('@confirmationForm').findByText(
/^Resend confirmation instructions$/i,
);
});
});
});

View file

@ -0,0 +1,77 @@
describe('User Change Password', () => {
beforeEach(() => {
cy.testSetup();
cy.fixture('users/changePasswordUser.json').as('user');
cy.get('@user').then((user) => {
cy.loginUser(user).then(() => {
cy.visit('/settings/account');
});
});
});
it('should change the password of a user', () => {
cy.fixture('users/changePasswordUser.json').as('user');
const newPassword = 'drowssap';
cy.findByTestId('update-password-form').as('updatePasswordForm');
cy.get('@user').then((user) => {
cy.get('@updatePasswordForm')
.findByText(/^Current Password$/i)
.type(user.password);
cy.get('@updatePasswordForm')
.findByText(/^Password$/i)
.type(newPassword);
cy.get('@updatePasswordForm')
.findByText(/^Confirm new password$/)
.type(newPassword);
// Submit the form
cy.get('@updatePasswordForm').findByText('Set New Password').click();
cy.findByTestId('login-form').as('loginForm');
cy.get('@loginForm')
.findByText(/^Email$/)
.type(user.email);
cy.get('@loginForm')
.findByText(/^Password$/)
.type(newPassword);
});
// Submit the form
cy.get('@loginForm').findByText('Continue').click();
const { baseUrl } = Cypress.config();
cy.url().should('equal', `${baseUrl}settings/account?signin=true`);
});
it('should give an error if the new password/confirm new password fields do not match when changing the password of a user', () => {
cy.fixture('users/changePasswordUser.json').as('user');
cy.get('@user').then((user) => {
cy.findByTestId('update-password-form').as('updatePasswordForm');
cy.get('@updatePasswordForm')
.findByText(/^Current Password$/i)
.type(user.password);
cy.get('@updatePasswordForm')
.findByText(/^Password$/i)
.type('some new password that is not the same');
cy.get('@updatePasswordForm')
.findByText(/^Confirm new password$/)
.type('some other new password that is not the same');
});
// Submit the form
cy.get('@updatePasswordForm')
.findByText(/^Set New Password$/)
.click();
cy.findByTestId('account-errors-panel').findByText(
/^Password doesn't match password confirmation$/,
);
});
});

View file

@ -0,0 +1,36 @@
describe('User Login', () => {
beforeEach(() => {
cy.testSetup();
});
it('should login a user', () => {
cy.fixture('users/changePasswordUser.json').as('user');
// Go to home page
cy.visit('/');
// Click on the login button in the top header
cy.findAllByText('Log in').first().click();
// Ensure we are redirected to the login page
cy.url().should('contains', '/enter');
cy.findByTestId('login-form').as('loginForm');
cy.get('@user').then((user) => {
cy.get('@loginForm')
.findByText(/^Email$/)
.type(user.email);
cy.get('@loginForm')
.findByText(/^Password$/)
.type(user.password);
});
// Submit the form
cy.get('@loginForm').findByText('Continue').click();
// User should be redirected to onboarding
const { baseUrl } = Cypress.config();
cy.url().should('equal', `${baseUrl}?signin=true`);
});
});

View file

@ -1,9 +1,6 @@
/// <reference types="cypress" />
/* eslint-env node */
const { spawn } = require('child_process');
const fetch = require('node-fetch');
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
@ -28,70 +25,5 @@ module.exports = (on, config) => {
...process.env,
};
on('task', {
async resetData() {
const clearSearchIndices = fetch(`${config.env.ELASTICSEARCH_URL}/*`, {
method: 'DELETE',
});
const truncateDB = new Promise((resolve, reject) => {
// Clear the DB for the next test run.
const child = spawn('bundle', ['exec', 'rails db:truncate_all']);
const errorChunks = [];
child.stderr.on('data', (chunk) =>
errorChunks.push(Buffer.from(chunk)),
);
child.on('error', (error) => {
reject(error);
});
child.on('exit', (status, _code) => {
if (status !== 0) {
const errorMessage = Buffer.concat(errorChunks).toString('UTF-8');
reject(
new Error(
`There was an error running "bundle exec rails db:truncate_all". The status was ${status} with the following error:\n${errorMessage}`,
),
);
}
resolve(status === 0);
});
});
const [clearSearchIndicesResponse, clearedDB = false] = await Promise.all(
[
clearSearchIndices,
truncateDB.catch((error) => {
throw new Error(error);
}),
],
);
const {
acknowledged = false,
error,
} = await clearSearchIndicesResponse.json();
if (!acknowledged || error) {
throw new Error(
`There was an error clearing indices in Elastic Search:\n${
error ? JSON.stringify(error, null, '\t') : ''
}`,
);
}
if (!clearedDB) {
throw new Error(`The database did not truncate successfully`);
}
// Nothing to do, we're all good.
// Cypress tasks require a return value, so returning null.
return null;
},
});
return config;
};

View file

@ -23,3 +23,42 @@
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
/**
* Runs necessary test setup to run a clean test.
*/
Cypress.Commands.add('testSetup', () => {
// Required for the moment because of https://github.com/cypress-io/cypress/issues/781
cy.clearCookies();
cy.getCookies().then((cookie) => {
if (cookie.length) {
// Instead of always waiting, only wait if the cookies aren't
// cleared yet and attempt to clear again.
cy.wait(500); // eslint-disable-line cypress/no-unnecessary-waiting
cy.clearCookies();
}
});
cy.request('/cypress_rails_reset_state');
});
/**
* Logs in a user with the given email and password.
*
* @param credentials
* @param credentials.email {string} An email address
* @param credentials.password {string} A password
*
* @returns {Cypress.Chainable<Cypress.Response>} A cypress request for signing in a user.
*/
Cypress.Commands.add('loginUser', ({ email, password }) => {
const encodedEmail = encodeURIComponent(email);
const encodedPassword = encodeURIComponent(password);
return cy.request(
'POST',
'/users/sign_in',
`utf8=%E2%9C%93&user%5Bemail%5D=${encodedEmail}&user%5Bpassword%5D=${encodedPassword}&user%5Bremember_me%5D=0&user%5Bremember_me%5D=1&commit=Continue`,
);
});

View file

@ -2,43 +2,9 @@
return if Rails.env.production?
# NOTE: when adding new data, please use this class to ensure the seed tasks
# NOTE: when adding new data, please use the Seeder class to ensure the seed tasks
# stays idempotent.
class Seeder
def initialize
@counter = 0
end
# Used when the block is idempotent by itself and needs no further checks.
def create(message)
@counter += 1
puts " #{@counter}. #{message}."
yield
end
def create_if_none(klass, count = nil)
@counter += 1
plural = klass.name.pluralize
if klass.none?
message = ["Creating", count, plural].compact.join(" ")
puts " #{@counter}. #{message}."
yield
else
puts " #{@counter}. #{plural} already exist. Skipping."
end
end
def create_if_doesnt_exist(klass, attribute_name, attribute_value)
record = klass.find_by("#{attribute_name}": attribute_value)
if record.nil?
puts " #{klass} with #{attribute_name} = #{attribute_value} not found, proceeding..."
yield
else
puts " #{klass} with #{attribute_name} = #{attribute_value} found, skipping."
end
end
end
require Rails.root.join("app/lib/seeder")
# we use this to be able to increase the size of the seeded DB at will
# eg.: `SEEDS_MULTIPLIER=2 rails db:seed` would double the amount of data

14
lib/tasks/e2e_seed.rake Normal file
View file

@ -0,0 +1,14 @@
# Thanks to https://stackoverflow.com/questions/19872271/adding-a-custom-seed-file/31815032#31815032
SEED_DIR = Rails.root.join("spec/support/seeds/")
namespace :db do
namespace :seed do
desc "Seed data for e2e tests"
task e2e: :environment do
raise "Attempting to seed production environment, aborting!" if Rails.env.production?
filename = SEED_DIR.join("seeds_e2e.rb")
load(filename) if File.exist?(filename)
end
end
end

View file

@ -25,10 +25,7 @@
"test": "jest app/javascript/ bin/ --coverage",
"test:watch": "jest app/javascript/ bin/ --watch",
"postcss": "postcss public/assets/*.css -d public/assets 2> postcss_error.log",
"e2e": "cypress open --config-file cypress.dev.json",
"e2e:runner": "bin/e2e",
"e2e:server": "bin/rails s -p 3000 -b 0.0.0.0",
"e2e:ci": "start-server-and-test e2e:server http://localhost:3000 e2e:runner"
"e2e": "bin/e2e"
},
"husky": {
"hooks": {
@ -88,7 +85,6 @@
},
"homepage": "https://github.com/thepracticaldev/dev.to#readme",
"devDependencies": {
"@knapsack-pro/cypress": "^4.2.1",
"@stoplight/spectral": "5.8.0",
"@storybook/addon-a11y": "^6.1.15",
"@storybook/addon-actions": "^6.1.15",
@ -135,7 +131,6 @@
"jsdom": "^16.4.0",
"lint-staged": "^10.5.3",
"markdown-loader": "^6.0.0",
"node-fetch": "^2.6.1",
"prettier": "^2.2.1",
"redoc-cli": "0.10.2",
"sass": "1.32.5",

View file

@ -0,0 +1,86 @@
return unless Rails.env.test? && ENV["E2E"].present?
# NOTE: when adding new data, please use the Seeder class to ensure the seed tasks
# stays idempotent.
require Rails.root.join("app/lib/seeder")
seeder = Seeder.new
##############################################################################
# Default development site config if different from production scenario
SiteConfig.public = true
SiteConfig.waiting_on_first_user = false
seeder.create_if_none(Organization) do
3.times do
Organization.create!(
name: Faker::TvShows::SiliconValley.company,
summary: Faker::Company.bs,
remote_profile_image_url: logo = Faker::Company.logo,
nav_image: logo,
url: Faker::Internet.url,
slug: "org#{rand(10_000)}",
github_username: "org#{rand(10_000)}",
twitter_username: "org#{rand(10_000)}",
bg_color_hex: Faker::Color.hex_color,
text_color_hex: Faker::Color.hex_color,
)
end
end
##############################################################################
# NOTE: @citizen428 For the time being we want all current DEV profile fields.
# The CSV import is idempotent by itself, since it uses find_or_create_by.
seeder.create("Creating DEV profile fields") do
dev_fields_csv = Rails.root.join("lib/data/dev_profile_fields.csv")
ProfileFields::ImportFromCsv.call(dev_fields_csv)
end
##############################################################################
seeder.create_if_doesnt_exist(User, "email", "admin@forem.local") do
user = User.create!(
name: "Admin McAdmin",
email: "admin@forem.local",
username: "Admin_McAdmin",
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
website_url: Faker::Internet.url,
email_comment_notifications: false,
email_follower_notifications: false,
confirmed_at: Time.current,
password: "password",
password_confirmation: "password",
saw_onboarding: true,
checked_code_of_conduct: true,
checked_terms_and_conditions: true,
)
user.add_role(:super_admin)
user.add_role(:single_resource_admin, Config)
end
##############################################################################
seeder.create_if_doesnt_exist(User, "email", "change-password-user@forem.com") do
User.create!(
name: "Change Password User",
email: "change-password-user@forem.com",
username: "changepassworduser",
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
website_url: Faker::Internet.url,
email_comment_notifications: false,
email_follower_notifications: false,
confirmed_at: Time.current,
password: "password",
password_confirmation: "password",
saw_onboarding: true,
checked_code_of_conduct: true,
checked_terms_and_conditions: true,
)
end
##############################################################################

BIN
vendor/cache/cypress-rails-0.4.2.gem vendored Normal file

Binary file not shown.

160
yarn.lock
View file

@ -1092,15 +1092,6 @@
debug "^3.1.0"
lodash.once "^4.1.1"
"@dabh/diagnostics@^2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.2.tgz#290d08f7b381b8f94607dc8f471a12c675f9db31"
integrity sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==
dependencies:
colorspace "1.1.x"
enabled "2.0.x"
kuler "^2.0.0"
"@emotion/babel-utils@^0.6.4":
version "0.6.10"
resolved "https://registry.yarnpkg.com/@emotion/babel-utils/-/babel-utils-0.6.10.tgz#83dbf3dfa933fae9fc566e54fbb45f14674c6ccc"
@ -1510,25 +1501,6 @@
"@types/yargs" "^15.0.0"
chalk "^4.0.0"
"@knapsack-pro/core@^3.1.1":
version "3.1.1"
resolved "https://registry.yarnpkg.com/@knapsack-pro/core/-/core-3.1.1.tgz#fe6f8c8c970cf3b80cc5410ae22f66f3c899ccf2"
integrity sha512-yn5PrrM7lAXMr+fr44COOvszzy19ZWoKwD9toRQoZICARfaYGXnYxxBEzdoxkOKk3tJTcYyHGNLc7pn94VNmbg==
dependencies:
axios "0.21.1"
axios-retry "^3.1.9"
winston "^3.3.3"
"@knapsack-pro/cypress@^4.2.1":
version "4.2.1"
resolved "https://registry.yarnpkg.com/@knapsack-pro/cypress/-/cypress-4.2.1.tgz#0eef46caf4c21c7c3fe198d1ef08a5eabdd6e37c"
integrity sha512-r2mdSTWj433GtoBwVFyP/bd1AzUxtY4Txr6H0qaUsBqEKWQ/YkEUQTk5mfQbeJeIl0ZbkTSKqQsRDu+VXqR9wg==
dependencies:
"@knapsack-pro/core" "^3.1.1"
glob "^7.1.6"
minimist "^1.2.5"
uuid "^8.3.1"
"@mdx-js/loader@^1.6.19":
version "1.6.22"
resolved "https://registry.yarnpkg.com/@mdx-js/loader/-/loader-1.6.22.tgz#d9e8fe7f8185ff13c9c8639c048b123e30d322c4"
@ -4039,7 +4011,7 @@ async@^2.6.2:
dependencies:
lodash "^4.17.14"
async@^3.0.0, async@^3.1.0, async@^3.2.0:
async@^3.0.0, async@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
@ -4106,20 +4078,6 @@ axe-core@^4.0.1, axe-core@^4.0.2:
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.1.1.tgz#70a7855888e287f7add66002211a423937063eaf"
integrity sha512-5Kgy8Cz6LPC9DJcNb3yjAXTu3XihQgEdnIg50c//zOC/MyLP0Clg+Y8Sh9ZjjnvBrDZU4DgXS9C3T9r4/scGZQ==
axios-retry@^3.1.9:
version "3.1.9"
resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-3.1.9.tgz#6c30fc9aeb4519aebaec758b90ef56fa03fe72e8"
integrity sha512-NFCoNIHq8lYkJa6ku4m+V1837TP6lCa7n79Iuf8/AqATAHYB0ISaAS1eyIenDOfHOLtym34W65Sjke2xjg2fsA==
dependencies:
is-retry-allowed "^1.1.0"
axios@0.21.1, axios@^0.21.1:
version "0.21.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
dependencies:
follow-redirects "^1.10.0"
axios@^0.18.0:
version "0.18.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.1.tgz#ff3f0de2e7b5d180e757ad98000f1081b87bcea3"
@ -4128,6 +4086,13 @@ axios@^0.18.0:
follow-redirects "1.5.10"
is-buffer "^2.0.2"
axios@^0.21.1:
version "0.21.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
dependencies:
follow-redirects "^1.10.0"
axobject-query@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
@ -6191,14 +6156,6 @@ color-string@^1.4.0, color-string@^1.5.2, color-string@^1.5.4:
color-name "^1.0.0"
simple-swizzle "^0.2.2"
color@3.0.x:
version "3.0.0"
resolved "https://registry.yarnpkg.com/color/-/color-3.0.0.tgz#d920b4328d534a3ac8295d68f7bd4ba6c427be9a"
integrity sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==
dependencies:
color-convert "^1.9.1"
color-string "^1.5.2"
color@^0.11.0:
version "0.11.4"
resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764"
@ -6237,19 +6194,11 @@ colorette@^1.2.1:
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b"
integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==
colors@^1.1.2, colors@^1.2.1:
colors@^1.1.2:
version "1.4.0"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
colorspace@1.1.x:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.2.tgz#e0128950d082b86a2168580796a0aa5d6c68d8c5"
integrity sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==
dependencies:
color "3.0.x"
text-hex "1.0.x"
combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
@ -7736,11 +7685,6 @@ emotion@^9.1.2, emotion@^9.1.3, emotion@^9.2.12:
babel-plugin-emotion "^9.2.11"
create-emotion "^9.2.12"
enabled@2.0.x:
version "2.0.0"
resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2"
integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
@ -8544,7 +8488,7 @@ fast-memoize@^2.5.1:
resolved "https://registry.yarnpkg.com/fast-memoize/-/fast-memoize-2.5.2.tgz#79e3bb6a4ec867ea40ba0e7146816f6cdce9b57e"
integrity sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==
fast-safe-stringify@^2.0.4, fast-safe-stringify@^2.0.7:
fast-safe-stringify@^2.0.7:
version "2.0.7"
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
@ -8584,11 +8528,6 @@ fd-slicer@~1.1.0:
dependencies:
pend "~1.2.0"
fecha@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.0.tgz#3ffb6395453e3f3efff850404f0a59b6747f5f41"
integrity sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==
figgy-pudding@^3.5.1:
version "3.5.2"
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e"
@ -8819,11 +8758,6 @@ flush-write-stream@^1.0.0:
inherits "^2.0.3"
readable-stream "^2.3.6"
fn.name@1.x.x:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc"
integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==
focus-lock@^0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/focus-lock/-/focus-lock-0.8.1.tgz#bb36968abf77a2063fa173cb6c47b12ac8599d33"
@ -10753,11 +10687,6 @@ is-resolvable@^1.0.0:
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==
is-retry-allowed@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4"
integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==
is-root@2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c"
@ -11706,11 +11635,6 @@ klona@^2.0.4:
resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0"
integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==
kuler@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3"
integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==
language-subtag-registry@~0.3.2:
version "0.3.21"
resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a"
@ -12125,17 +12049,6 @@ log-update@^4.0.0:
slice-ansi "^4.0.0"
wrap-ansi "^6.2.0"
logform@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/logform/-/logform-2.2.0.tgz#40f036d19161fc76b68ab50fdc7fe495544492f2"
integrity sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==
dependencies:
colors "^1.2.1"
fast-safe-stringify "^2.0.4"
fecha "^4.2.0"
ms "^2.1.1"
triple-beam "^1.3.0"
loglevel@^1.6.8:
version "1.7.1"
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197"
@ -12919,7 +12832,7 @@ node-fetch-h2@^2.3.0:
dependencies:
http2-client "^1.2.5"
node-fetch@2.6, node-fetch@2.6.1, node-fetch@^2.6.0, node-fetch@^2.6.1:
node-fetch@2.6, node-fetch@2.6.1, node-fetch@^2.6.0:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
@ -13370,13 +13283,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
dependencies:
wrappy "1"
one-time@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45"
integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==
dependencies:
fn.name "1.x.x"
onecolor@^3.0.4:
version "3.1.0"
resolved "https://registry.yarnpkg.com/onecolor/-/onecolor-3.1.0.tgz#b72522270a49569ac20d244b3cd40fe157fda4d2"
@ -16061,7 +15967,7 @@ read-pkg@^5.2.0:
parse-json "^5.0.0"
type-fest "^0.6.0"
"readable-stream@1 || 2", readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@^2.3.7, readable-stream@~2.3.6:
"readable-stream@1 || 2", readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@ -16084,7 +15990,7 @@ readable-stream@1.1.x:
isarray "0.0.1"
string_decoder "~0.10.x"
readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
@ -17549,11 +17455,6 @@ stable@^0.1.8:
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
stack-trace@0.0.x:
version "0.0.10"
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=
stack-utils@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277"
@ -18213,11 +18114,6 @@ test-exclude@^6.0.0:
glob "^7.1.4"
minimatch "^3.0.4"
text-hex@1.0.x:
version "1.0.0"
resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5"
integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==
text-table@0.2, text-table@0.2.0, text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@ -18443,11 +18339,6 @@ trim@0.0.1:
resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd"
integrity sha1-WFhUf2spB1fulczMZm+1AITEYN0=
triple-beam@^1.2.0, triple-beam@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9"
integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==
trough@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406"
@ -18991,7 +18882,7 @@ uuid@^3.3.2, uuid@^3.4.0:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
uuid@^8.0.0, uuid@^8.3.0, uuid@^8.3.1:
uuid@^8.0.0, uuid@^8.3.0:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
@ -19407,29 +19298,6 @@ widest-line@^3.1.0:
dependencies:
string-width "^4.0.0"
winston-transport@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.4.0.tgz#17af518daa690d5b2ecccaa7acf7b20ca7925e59"
integrity sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==
dependencies:
readable-stream "^2.3.7"
triple-beam "^1.2.0"
winston@^3.3.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/winston/-/winston-3.3.3.tgz#ae6172042cafb29786afa3d09c8ff833ab7c9170"
integrity sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==
dependencies:
"@dabh/diagnostics" "^2.0.2"
async "^3.1.0"
is-stream "^2.0.0"
logform "^2.2.0"
one-time "^1.0.0"
readable-stream "^3.4.0"
stack-trace "0.0.x"
triple-beam "^1.3.0"
winston-transport "^4.4.0"
wolfy87-eventemitter@~5.2.8:
version "5.2.9"
resolved "https://registry.yarnpkg.com/wolfy87-eventemitter/-/wolfy87-eventemitter-5.2.9.tgz#e879f770b30fbb6512a8afbb330c388591099c2a"