diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index b0ce4f4..e90c3bb 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -44,9 +44,6 @@ jobs: GITHUB_CLIENT_SECRET: ${{ secrets.GITHUB_CLIENT_SECRET }} run: docker-compose -f docker-compose.testing.yml run cypress - - name: Tear Down Test Environment - run: docker-compose -f docker-compose.testing.yml down - build-and-push: needs: - ci diff --git a/app/src/store/session.js b/app/src/store/session.js index 4c85f76..ac64e88 100644 --- a/app/src/store/session.js +++ b/app/src/store/session.js @@ -4,6 +4,7 @@ import { FileGraph } from '@linux-odyssey/file-graph' import api from '../utils/api' import Socket from '../utils/socket' import SocketTerminal from '../utils/terminal' +import { LoadQuestError, LoadSessionError } from '../utils/errors' const toast = useToast() @@ -32,7 +33,7 @@ const socket = new Socket() const term = new SocketTerminal(40, 80) function setQuest(questId) { - api.get(`/quests/${questId}`).then((res) => { + return api.get(`/quests/${questId}`).then((res) => { store.questId = res.data._id store.quest = res.data }) @@ -108,9 +109,20 @@ export function reset() { } export async function init(questId) { + if (!questId) throw new Error('No quest ID provided') reset() - await setQuest(questId) - await getActiveSession(questId) + try { + await setQuest(questId) + } catch (err) { + console.error(err) + throw new LoadQuestError('Failed to load quest', questId) + } + try { + await getActiveSession(questId) + } catch (err) { + console.error(err) + throw new LoadSessionError('Failed to load session', questId) + } } function setup() { diff --git a/app/src/utils/errors.js b/app/src/utils/errors.js index 2fe0cbb..ff8af71 100644 --- a/app/src/utils/errors.js +++ b/app/src/utils/errors.js @@ -20,8 +20,18 @@ export class UnauthorizedError extends Error { } } -export default { - TooManyRequestsError, - ValidationError, - UnauthorizedError, +export class LoadQuestError extends Error { + constructor(message, questId) { + super(message) + this.name = 'LoadQuestError' + this.questId = questId + } +} + +export class LoadSessionError extends Error { + constructor(message, questId) { + super(message) + this.name = 'LoadSessionError' + this.questId = questId + } } diff --git a/app/src/utils/socket.js b/app/src/utils/socket.js index b93e145..56b12da 100644 --- a/app/src/utils/socket.js +++ b/app/src/utils/socket.js @@ -42,6 +42,10 @@ class SocketWrapper { } emit(event, data) { + if (!this.socket) { + console.warn('Socket is not connected!') + return + } this.socket.emit(event, data) } diff --git a/app/src/views/GamePage.vue b/app/src/views/GamePage.vue index d34a80c..5e49e12 100644 --- a/app/src/views/GamePage.vue +++ b/app/src/views/GamePage.vue @@ -10,6 +10,7 @@ import TerminalPart from '../components/TerminalPart.vue' import VisualizationPart from '../components/VisualizationPart.vue' import ControlPalette from '../components/ControlPalette.vue' import CompleteModal from '../components/CompleteModal.vue' +import { LoadQuestError, LoadSessionError } from '../utils/errors' const completed = computed(() => { return sessionStore.session.status === 'finished' @@ -28,8 +29,18 @@ onMounted(async () => { try { await init(props.questId) } catch (err) { + if (err instanceof LoadQuestError) { + toast.error(`Failed to load quest: ${err.questId}. Please check the URL.`) + return + } + if (err instanceof LoadSessionError) { + toast.error( + `Failed to create session for quest: ${err.questId}. Please re-login and try again.` + ) + return + } console.error(err) - toast.error('Failed to initialize game session') + toast.error(err.message) } }) diff --git a/docker-compose.testing.yml b/docker-compose.testing.yml index b0ff2f3..1fdf51d 100644 --- a/docker-compose.testing.yml +++ b/docker-compose.testing.yml @@ -1,14 +1,17 @@ version: '3' services: cypress: - image: cypress/included + build: + context: . + dockerfile: tests/Dockerfile networks: - internal volumes: - - ./tests:/e2e - working_dir: /e2e + - ./tests:/e2e/tests environment: - BASE_URL=http://app-test + - TESTING_USERNAME=alex + - TESTING_PASSWORD=Alex1234 depends_on: - app-test @@ -49,6 +52,10 @@ services: - GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET} - SURVEY_URL=${SURVEY_URL} - BUG_REPORT_URL=${BUG_REPORT_URL} + - NODE_ENV=development + - TESTING=true + - TESTING_USERNAME=alex + - TESTING_PASSWORD=Alex1234 db-test: image: mongo diff --git a/docker-compose.yml b/docker-compose.yml index e509387..c991351 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -45,6 +45,9 @@ services: - GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET} - GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID} - GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET} + - TESTING=true + - TESTING_USERNAME=alex + - TESTING_PASSWORD=Alex1234 analytics: build: diff --git a/package.json b/package.json index fb0741a..f248597 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "format": "prettier --ignore-path .prettierignore --write app server", "test": "yarn node --experimental-vm-modules $(yarn bin jest)", "test:e2e": "yarn workspace tests test", + "test:open": "yarn workspace tests test:open", "analytics": "yarn workspace analytics dev", "setup": "yarn workspace server setup", "clean": "yarn workspace server clean", diff --git a/quests/helloworld/answer.sh b/quests/helloworld/answer.sh new file mode 100644 index 0000000..73d9176 --- /dev/null +++ b/quests/helloworld/answer.sh @@ -0,0 +1,4 @@ +echo help{enter} +ls {enter} +cat forgotten_scroll.txt{enter} +echo Hello World!{enter} \ No newline at end of file diff --git a/server/src/api/controllers/authController.js b/server/src/api/controllers/authController.js index 6ac6a1f..8ae7978 100644 --- a/server/src/api/controllers/authController.js +++ b/server/src/api/controllers/authController.js @@ -1,6 +1,7 @@ import { matchedData } from 'express-validator' import { User } from '@linux-odyssey/models' -import { genJWT, hashPassword } from '../../utils/auth.js' +import { genJWT } from '../../utils/auth.js' +import { createUser } from '../../models/userManager.js' import { asyncHandler } from '../../middleware/error.js' export const issueToken = asyncHandler(async (req, res) => { @@ -16,12 +17,7 @@ export const issueToken = asyncHandler(async (req, res) => { export const register = asyncHandler(async (req, res, next) => { const { username, password, email } = matchedData(req) - const user = new User({ - username, - email, - }) - user.hashedPassword = await hashPassword(password) - await user.save() + const user = await createUser(username, email, password) req.login(user, (err) => { if (err) { next(err) diff --git a/server/src/api/controllers/commandController.js b/server/src/api/controllers/commandController.js index fa8de98..c956117 100644 --- a/server/src/api/controllers/commandController.js +++ b/server/src/api/controllers/commandController.js @@ -2,7 +2,7 @@ import { Command, Session } from '@linux-odyssey/models' import { matchedData } from 'express-validator' import CommandHandler from '../../game/commandHandler.js' import { pushToSession } from '../socket.js' -import { finishSession } from '../../game/sessionManager.js' +import { finishSession } from '../../models/sessionManager.js' import { asyncHandler } from '../../middleware/error.js' const commandCompleteCallbacks = new Map() diff --git a/server/src/api/controllers/sessionController.js b/server/src/api/controllers/sessionController.js index 5ae0d53..05188fb 100644 --- a/server/src/api/controllers/sessionController.js +++ b/server/src/api/controllers/sessionController.js @@ -4,7 +4,7 @@ import { createNewSession, getOrCreateActiveSession, isQuestUnlocked, -} from '../../game/sessionManager.js' +} from '../../models/sessionManager.js' import { asyncHandler } from '../../middleware/error.js' function sessionSummary(session) { diff --git a/server/src/config.js b/server/src/config.js index b904580..eb0b7b9 100644 --- a/server/src/config.js +++ b/server/src/config.js @@ -46,5 +46,10 @@ const config = { } config.baseUrl = get('BASE_URL', `http://${config.host}:${config.port}`) +config.testing = { + enabled: !config.isProduction && process.env.TESTING === 'true', + username: get('TESTING_USERNAME', ''), + password: get('TESTING_PASSWORD', ''), +} export default config diff --git a/server/src/index.js b/server/src/index.js index fda82b1..9e1fcbe 100644 --- a/server/src/index.js +++ b/server/src/index.js @@ -15,6 +15,7 @@ import errorHandler from './middleware/error.js' import { globalRateLimit } from './middleware/rateLimiter.js' import sessionMiddleware from './middleware/session.js' import expiryRemovalScheduler from './containers/expiryChecker.js' +import setupTest from './utils/setupTest.js' async function main() { if (!config.secret) { @@ -33,6 +34,10 @@ async function main() { process.exit(1) } + if (config.testing.enabled) { + await setupTest() + } + await loadAndUpdateQuests() expiryRemovalScheduler() diff --git a/server/src/middleware/rateLimiter.js b/server/src/middleware/rateLimiter.js index 85b1f5d..f8dcc32 100644 --- a/server/src/middleware/rateLimiter.js +++ b/server/src/middleware/rateLimiter.js @@ -1,16 +1,24 @@ import rateLimit from 'express-rate-limit' +import config from '../config.js' -export const globalRateLimit = rateLimit({ +function createRateLimiter(options) { + if (config.testing.enabled) { + return (req, res, next) => next() + } + return rateLimit(options) +} + +export const globalRateLimit = createRateLimiter({ windowMs: 60 * 1000, // 1 minute max: 100, }) -export const authenticateRateLimit = rateLimit({ +export const authenticateRateLimit = createRateLimiter({ windowMs: 2 * 60 * 1000, max: 5, }) -export const sessionRateLimit = rateLimit({ +export const sessionRateLimit = createRateLimiter({ windowMs: 10 * 1000, max: 1, keyGenerator: (req) => { diff --git a/server/src/game/sessionManager.js b/server/src/models/sessionManager.js similarity index 98% rename from server/src/game/sessionManager.js rename to server/src/models/sessionManager.js index 97ef708..8a9bbb9 100644 --- a/server/src/game/sessionManager.js +++ b/server/src/models/sessionManager.js @@ -1,6 +1,6 @@ import { Session, Quest, UserProfile } from '@linux-odyssey/models' import { createContainer, deleteContainer } from '../containers/docker.js' -import SessionHandler from './sessionHandler.js' +import SessionHandler from '../game/sessionHandler.js' async function deactivateSessions(user, quest) { const sessions = await Session.find({ diff --git a/server/src/models/userManager.js b/server/src/models/userManager.js new file mode 100644 index 0000000..aa877fb --- /dev/null +++ b/server/src/models/userManager.js @@ -0,0 +1,16 @@ +import { User, UserProfile } from '@linux-odyssey/models' +import { hashPassword } from '../utils/auth.js' + +export async function createUser(username, email, password) { + const user = new User({ + username, + email, + }) + user.hashedPassword = await hashPassword(password) + await user.save() + + const userProfile = new UserProfile({ + user, + }) + await userProfile.save() +} diff --git a/server/src/utils/setupTest.js b/server/src/utils/setupTest.js new file mode 100644 index 0000000..2c97278 --- /dev/null +++ b/server/src/utils/setupTest.js @@ -0,0 +1,22 @@ +import { User } from '@linux-odyssey/models' +import config from '../config.js' +import { createUser } from '../models/userManager.js' + +export default async function setupTest() { + console.log('Setting up test account') + if (!(config.testing.username && config.testing.password)) { + throw new Error( + 'No testing account found! Please set TESTING_USERNAME and TESTING_PASSWORD in .env' + ) + } + const { username, password } = config.testing + const email = `${username}@example.com` + + if (await User.exists({ username })) { + console.log('Testing account already exists') + return + } + + await createUser(username, email, password) + console.log('Created testing account') +} diff --git a/tests/Dockerfile b/tests/Dockerfile new file mode 100644 index 0000000..0e35d91 --- /dev/null +++ b/tests/Dockerfile @@ -0,0 +1,10 @@ +FROM cypress/included:13.5.1 + +WORKDIR /e2e +COPY package.json yarn.lock ./ +COPY tests/package.json ./tests/package.json +RUN yarn install --frozen-lockfile + +COPY quests ./quests +COPY tests ./tests +WORKDIR /e2e/tests \ No newline at end of file diff --git a/tests/cypress.config.js b/tests/cypress.config.js index 3a0330e..9057bb2 100644 --- a/tests/cypress.config.js +++ b/tests/cypress.config.js @@ -1,12 +1,36 @@ const { defineConfig } = require('cypress') const process = require('process') +require('dotenv').config({ path: '../.env' }) const getConfig = (key, defaultValue) => { return process.env[key] || defaultValue } +const getOrFail = (key) => { + const value = getConfig(key) + if (!value) { + throw new Error(`Missing required environment variable ${key}`) + } + return value +} + module.exports = defineConfig({ e2e: { baseUrl: getConfig('BASE_URL', 'http://localhost:5173'), + viewportWidth: 1920, + viewportHeight: 1080, + pageLoadTimeout: 120000, + }, + env: { + defaultAccount: getOrFail('TESTING_USERNAME'), + defaultPassword: getOrFail('TESTING_PASSWORD'), + // google_username: process.env.GOOGLE_USERNAME, + // google_password: process.env.GOOGLE_PASSWORD, + // google_client_id: process.env.GOOGLE_CLIENT_ID, + // google_client_secret: process.env.GOOGLE_CLIENT_SECRET, + // github_username: process.env.GITHUB_USERNAME, + // github_password: process.env.GITHUB_PASSWORD, + // github_client_id: process.env.GITHUB_CLIENT_ID, + // github_client_secret: process.env.GITHUB_CLIENT_SECRET, }, }) diff --git a/tests/cypress/e2e/helloworld.cy.js b/tests/cypress/e2e/helloworld.cy.js index 97cffef..5894660 100644 --- a/tests/cypress/e2e/helloworld.cy.js +++ b/tests/cypress/e2e/helloworld.cy.js @@ -1,10 +1,186 @@ /// +export default function checkLoginUI() { + cy.get('h1.text-text-primary') + .should('contain', 'Linux Odyssey') + .and('be.visible') + cy.get('#password') + .invoke('attr', 'placeholder') + .should('contain', 'Password') +} describe('example helloworld app', () => { - it('open login page', () => { - console.log('baseUrl', Cypress.config('baseUrl')) - cy.visit('/register') - cy.get('#username').type('alex') - cy.get('#email').type('alex@gmail.com') - cy.get('#password').type('123456As{enter}') + describe('Login tests', () => { + beforeEach(() => { + cy.visit('/') + }) + it('Check Login Page Elements-Register', () => { + checkLoginUI() + cy.get('#username') + .invoke('attr', 'placeholder') + .should('contain', 'Username') + cy.get('label.text-text-secondary').should('be.visible') // check the rules is displayed + cy.get('#email').invoke('attr', 'placeholder').should('contain', 'Email') + cy.get('span').contains('Already have an account?').should('be.visible') + cy.get('a.text-text-primary') + .should('contain', 'Log in') + .and('be.visible') + }) + it('Check Login Page Elements-Login', () => { + cy.get('a.text-text-primary') + .should('contain', 'Log in') + .and('be.visible') + .click() + checkLoginUI() + cy.get('#username') + .invoke('attr', 'placeholder') + .should('contain', 'Email / Username') + cy.findByRole('button', { name: 'Log In' }).should('be.visible') + cy.get('span').contains("Don't have an account?").should('be.visible') + cy.get('a.text-text-primary') + .should('contain', 'Sign up') + .and('be.visible') + }) + it('Check social account UI', () => { + cy.get('p').contains('or').should('be.visible') + cy.findByRole('link', { name: 'Continue with Google' }).should( + 'be.visible' + ) + cy.findByRole('link', { name: 'Continue with GitHub' }).should( + 'be.visible' + ) + }) + it('Check login fail', () => { + cy.LoginWithPassword('dddd', '123456') + cy.get('p').contains('Wrong username or password.').should('be.visible') + cy.LoginWithPassword(`ddd\``, '123456') + cy.get('p').contains('Invalid username or password.').should('be.visible') + }) + it('Login-Password Already Register', () => { + cy.LoginWithPassword( + Cypress.env('defaultAccount'), + Cypress.env('defaultPassword') + ) + }) + }) + describe('Game Start Page UI', () => { + beforeEach(() => { + cy.PrepareForGame() + }) + it('Check Header', () => { + cy.findByRole('button', { name: 'Hello, Linux World!' }).should( + 'be.visible' + ) + cy.findByRole('link', { name: 'Bug Report' }).should('be.visible') + cy.findByRole('button', { name: 'Sign Out' }).should('be.visible') + }) + it('Check QuestInfo', () => { + cy.get('#topic').should('contain', 'Hello, Linux World!') + cy.get('#quest').find('p.text-text').should('have.length', 3) + cy.getQuestInfo('Tasks:').should('be.visible') + cy.getQuestInfo('輸入 `echo help` 來朗誦咒語').should('be.visible') + }) + it('Check Command Cheat Sheet', () => { + cy.get('#cmdlist').within(($cmdlist) => { + cy.get($cmdlist).should('be.visible') + cy.get('svg[data-icon="list"]').should('be.visible') + cy.get('h1').should('contain', 'Command Cheatsheets').and('be.visible') + cy.get('#cheatsheets') + .should('contain', '--Command List--') + .and('be.visible') + }) + }) + it('Check Terminal', () => { + cy.get('#terminal', { timeout: 20000 }).within(($terminal) => { + cy.get($terminal).should('be.visible') + cy.get('svg[data-icon="terminal"]').should('be.visible') + cy.findByRole('button').should('contain', 'Terminal').and('be.visible') + cy.get('@Terminaltextbox') + .should('be.visible') + .and('contain', 'commander:~ $') + }) + }) + it('Check Hint Part', () => { + cy.get('#hint', { timeout: 20000 }).within(($hint) => { + cy.get($hint).should('be.visible') + cy.get('svg[data-icon="lightbulb"]').should('be.visible') + cy.get('h1').should('contain', 'Hint').and('be.visible') + }) + }) + it('Check File TreeChart', () => { + cy.get('#tree').should('be.visible') + cy.get('#tree').get('a').should('contain', '???').and('be.visible') + }) + it('Check Fucnctional Buttons', () => { + cy.findByRole('button', { name: 'Solution' }, { timeout: 30000 }).should( + 'be.visible' + ) + cy.findByRole('button', { name: 'Reset' }).should('be.visible') + cy.findByRole('button', { name: 'Continue' }).should('be.visible') + }) + }) + describe('Game Play', () => { + beforeEach(() => { + cy.PrepareForGame() + cy.readFile('../quests/helloworld/answer.sh', 'utf-8').as('answers') + }) + it('Typing in Terminal', () => { + cy.typeInCommand('12345{enter}') + cy.get('@Terminaltextbox') + .should('contain', 'zsh: command not found: 12345') + .and('contain', '12345') + }) + it.only('Complete the Game(relating UI)', () => { + // get answersheet + cy.get('@answers').then((answers) => { + const answerarr = answers.split('\n') + // Stage1 + cy.log('Stage1') + cy.typeInCommand(answerarr[0]) + cy.checkPending() + cy.waitUntilActive() + cy.get('#Lbutton').should('be.visible').and('be.disabled') + cy.get('#Rbutton').should('be.visible').and('be.disabled') + cy.checkHint(1, 1) + cy.getQuestInfo('✓ 輸入 `echo help` 來朗誦咒語').should('be.visible') + cy.getQuestInfo('搜索卷軸').should('be.visible') + // Stage2 + cy.log('Stage2') + cy.typeInCommand(answerarr[1]) + cy.checkPending() + cy.waitUntilActive() + cy.get('#tree') + .get('a') + .should('contain', 'forgotten_scroll.txt') + .and('be.visible') + cy.get('#Lbutton').should('be.visible').and('be.enabled') + cy.get('#Rbutton').should('be.visible').and('be.disabled') + cy.checkHint(2, 2) + cy.get('#Lbutton').click() + cy.get('#Rbutton').should('be.enabled') + cy.getQuestInfo('✓ 搜索卷軸').should('be.visible') + cy.getQuestInfo('查看卷軸').should('be.visible') + // Stage3 + cy.log('Stage3') + cy.typeInCommand(answerarr[2]) + cy.checkPending() + cy.waitUntilActive() + cy.getQuestInfo('✓ 查看卷軸').should('be.visible') + cy.getQuestInfo('解除封印').should('be.visible') + cy.checkHint(3, 3) + // Stage4 + cy.log('Stage4') + cy.typeInCommand(answerarr[3]) + cy.checkPending() + cy.waitUntilActive() + cy.getQuestInfo('✓ 解除封印').should('be.visible') + cy.findByRole( + 'heading', + { name: 'Quest Completed!' }, + { timeout: 20000 } + ).should('be.visible') + // Check survey dialog pop up + cy.get('div[class="modal"]').find('p').should('be.visible') + cy.findByRole('link', { name: '填寫問卷' }).should('be.visible') + }) + }) }) }) diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 119ab03..fa1d735 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -1,25 +1,68 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add('login', (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) +import '@testing-library/cypress/add-commands' + +Cypress.Commands.add('LoginWithPassword', (username, password) => { + cy.clearAllCookies({ domain: null }) + cy.visit('/login') + cy.get('#username').type(username) + cy.get('#password').type(password) + cy.findByRole('button', { name: 'Log In' }).click() + cy.url().should('include', '/game') +}) +Cypress.Commands.add('PrepareForGame', () => { + cy.visit('/') + cy.LoginWithPassword( + Cypress.env('defaultAccount'), + Cypress.env('defaultPassword') + ) + cy.visit('/game/helloworld') + cy.get('.xterm-screen', { timeout: 10000 }) + .as('Terminaltextbox') + .should('be.visible') + cy.findByRole('button', { name: 'Reset' }).click() + cy.typeInCommand('clear{enter}') + cy.get('@Terminaltextbox', { timeout: 150000 }).should( + 'contain', + 'commander:~ $' + ) + cy.get('@Terminaltextbox', { timeout: 150000 }).should( + 'contain', + 'commander:~ $' + ) +}) +Cypress.Commands.add('typeInCommand', (command) => { + cy.get('.xterm-screen', { timeout: 150000 }).type(command) +}) +Cypress.Commands.add('getQuestInfo', (id) => { + return cy + .get('#quest') + .find('li') + .find('p') + .contains(`${id}`, { timeout: 100000 }) +}) +Cypress.Commands.add('checkHint', (index, total) => { + cy.get('#hint') + .find('.justify-end') + .contains(`${index}/${total}`) + .should('be.visible') +}) +Cypress.Commands.add('waitUntilActive', () => { + cy.get('input[id="currentStatus"]', { timeout: 1000000 }) + .invoke('val') + .then((value) => { + if (value === 'active' || value === 'finished') { + cy.log('story ends') + return + } + cy.typeInCommand('{enter}') + cy.waitUntilActive() + }) +}) +Cypress.Commands.add('checkPending', () => { + cy.get('input[id="currentStatus"]', { timeout: 1000000 }).should( + 'have.value', + 'pending' + ) +}) +Cypress.Commands.add('Complete the Stage (only command)', () => { + cy.log('') +}) diff --git a/tests/package.json b/tests/package.json index af8e1fa..be6c38b 100644 --- a/tests/package.json +++ b/tests/package.json @@ -5,9 +5,12 @@ "main": "index.js", "license": "MIT", "scripts": { - "test": "cypress run" + "test": "cypress run", + "test:open": "cypress open" }, "dependencies": { - "cypress": "^13.2.0" + "cypress": "^13.5.0", + "@testing-library/cypress": "^10.0.1", + "dotenv": "^16.3.1" } } diff --git a/yarn.lock b/yarn.lock index 851ae5b..19a6782 100644 --- a/yarn.lock +++ b/yarn.lock @@ -27,7 +27,7 @@ dependencies: "@babel/highlight" "^7.22.5" -"@babel/code-frame@^7.22.13": +"@babel/code-frame@^7.10.4", "@babel/code-frame@^7.22.13": version "7.22.13" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== @@ -206,7 +206,12 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" integrity sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q== -"@babel/parser@^7.22.15", "@babel/parser@^7.23.0": +"@babel/parser@^7.22.15": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.3.tgz#0ce0be31a4ca4f1884b5786057cadcb6c3be58f9" + integrity sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw== + +"@babel/parser@^7.23.0": version "7.23.0" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== @@ -309,6 +314,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/runtime@^7.12.5", "@babel/runtime@^7.14.6": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885" + integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.22.15": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" @@ -352,7 +364,16 @@ "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" -"@babel/types@^7.22.15", "@babel/types@^7.23.0": +"@babel/types@^7.22.15": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.3.tgz#d5ea892c07f2ec371ac704420f4dcdb07b5f9598" + integrity sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + +"@babel/types@^7.23.0": version "7.23.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== @@ -948,6 +969,33 @@ resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== +"@testing-library/cypress@^10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@testing-library/cypress/-/cypress-10.0.1.tgz#15abae0edb83237316ec6d07e152b71a50b38387" + integrity sha512-e8uswjTZIBhaIXjzEcrQQ8nHRWHgZH7XBxKuIWxZ/T7FxfWhCR48nFhUX5nfPizjVOKSThEfOSv67jquc1ASkw== + dependencies: + "@babel/runtime" "^7.14.6" + "@testing-library/dom" "^9.0.0" + +"@testing-library/dom@^9.0.0": + version "9.3.3" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.3.3.tgz#108c23a5b0ef51121c26ae92eb3179416b0434f5" + integrity sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^5.0.1" + aria-query "5.1.3" + chalk "^4.1.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.5.0" + pretty-format "^27.0.2" + +"@types/aria-query@^5.0.1": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" + integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== + "@types/babel__core@^7.1.14": version "7.20.1" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.1.tgz#916ecea274b0c776fec721e333e55762d3a9614b" @@ -1336,6 +1384,13 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +aria-query@5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" + integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== + dependencies: + deep-equal "^2.0.5" + array-buffer-byte-length@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" @@ -1719,6 +1774,15 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" +call-bind@^1.0.4, call-bind@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== + dependencies: + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -2027,10 +2091,10 @@ csstype@^3.1.1: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== -cypress@^13.2.0: - version "13.2.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.2.0.tgz#10f73d06a0764764ffbb903a31e96e2118dcfc1d" - integrity sha512-AvDQxBydE771GTq0TR4ZUBvv9m9ffXuB/ueEtpDF/6gOcvFR96amgwSJP16Yhqw6VhmwqspT5nAGzoxxB+D89g== +cypress@^13.5.0: + version "13.5.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.5.0.tgz#8c149074186130972f08b2cdce6ded41f014bacd" + integrity sha512-oh6U7h9w8wwHfzNDJQ6wVcAeXu31DlIYlNOBvfd6U4CcB8oe4akawQmH+QJVOMZlM42eBoCne015+svVqdwdRQ== dependencies: "@cypress/request" "^3.0.0" "@cypress/xvfb" "^1.2.4" @@ -2114,6 +2178,30 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== +deep-equal@^2.0.5: + version "2.2.3" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1" + integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.5" + es-get-iterator "^1.1.3" + get-intrinsic "^1.2.2" + is-arguments "^1.1.1" + is-array-buffer "^3.0.2" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.13" + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -2142,6 +2230,15 @@ default-browser@^4.0.0: execa "^7.1.1" titleize "^3.0.0" +define-data-property@^1.0.1, define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + define-lazy-prop@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" @@ -2233,6 +2330,16 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-accessibility-api@^0.5.9: + version "0.5.16" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" + integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== + +dotenv@^16.3.1: + version "16.3.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" + integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== + eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" @@ -2383,6 +2490,21 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: unbox-primitive "^1.0.2" which-typed-array "^1.1.10" +es-get-iterator@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + es-set-tostringtag@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" @@ -3037,6 +3159,11 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + function.prototype.name@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" @@ -3087,6 +3214,16 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@ has-proto "^1.0.1" has-symbols "^1.0.3" +get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== + dependencies: + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -3288,6 +3425,13 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -3402,6 +3546,15 @@ ini@2.0.0: resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== +internal-slot@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930" + integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg== + dependencies: + get-intrinsic "^1.2.2" + hasown "^2.0.0" + side-channel "^1.0.4" + internal-slot@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" @@ -3421,6 +3574,14 @@ ipaddr.js@1.9.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" @@ -3476,7 +3637,7 @@ is-core-module@^2.11.0: dependencies: has "^1.0.3" -is-date-object@^1.0.1: +is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== @@ -3530,6 +3691,11 @@ is-installed-globally@~0.4.0: global-dirs "^3.0.0" is-path-inside "^3.0.2" +is-map@^2.0.1, is-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== + is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" @@ -3560,6 +3726,11 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-set@^2.0.1, is-set@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== + is-shared-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" @@ -3612,6 +3783,11 @@ is-unicode-supported@^0.1.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== + is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -3619,6 +3795,14 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +is-weakset@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" + integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" @@ -3626,6 +3810,11 @@ is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -4345,6 +4534,11 @@ luxon@^3.2.1: resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.4.1.tgz#9147374b2c539e7893f906c420e9b080b59c5457" integrity sha512-2USspxOCXWGIKHwuQ9XElxPPYrDOJHDQ5DQ870CoD+CxJbBnRDIBCfhioUJJjct7BKOy80Ia8cVstIcIMb/0+Q== +lz-string@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== + magic-string@^0.30.0: version "0.30.1" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.1.tgz#ce5cd4b0a81a5d032bd69aab4522299b2166284d" @@ -4729,6 +4923,14 @@ object-inspect@^1.12.3, object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== +object-is@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -5101,6 +5303,15 @@ pretty-bytes@^5.6.0: resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== +pretty-format@^27.0.2: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + pretty-format@^29.6.1: version "29.6.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.1.tgz#ec838c288850b7c4f9090b867c2d4f4edbfb0f3e" @@ -5213,6 +5424,11 @@ raw-body@2.5.1: iconv-lite "0.4.24" unpipe "1.0.0" +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + react-is@^18.0.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" @@ -5241,6 +5457,11 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +regenerator-runtime@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" + integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== + regexp.prototype.flags@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" @@ -5250,6 +5471,15 @@ regexp.prototype.flags@^1.5.0: define-properties "^1.2.0" functions-have-names "^1.2.3" +regexp.prototype.flags@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" + integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + set-function-name "^2.0.0" + request-progress@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" @@ -5423,6 +5653,25 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-function-length@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" + integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== + dependencies: + define-data-property "^1.1.1" + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +set-function-name@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" + integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== + dependencies: + define-data-property "^1.0.1" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.0" + setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" @@ -5628,6 +5877,13 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -6244,6 +6500,16 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + which-typed-array@^1.1.10: version "1.1.10" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.10.tgz#74baa2789991905c2076abb317103b866c64e69e" @@ -6256,6 +6522,17 @@ which-typed-array@^1.1.10: has-tostringtag "^1.0.0" is-typed-array "^1.1.10" +which-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" + integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.4" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"