From 103a8dbd0f0a643c7e4cb5f149445ec5de4a8be8 Mon Sep 17 00:00:00 2001 From: Annie Huang Date: Mon, 13 Nov 2023 22:14:26 +0800 Subject: [PATCH 01/17] update config (cherry picked from commit ccb19b5bbdc7d5adee05a2a77598c945783eb5b4) --- tests/cypress.config.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/cypress.config.js b/tests/cypress.config.js index 3a0330e..98002f1 100644 --- a/tests/cypress.config.js +++ b/tests/cypress.config.js @@ -8,5 +8,21 @@ const getConfig = (key, defaultValue) => { module.exports = defineConfig({ e2e: { baseUrl: getConfig('BASE_URL', 'http://localhost:5173'), + viewportWidth: 1920, + viewportHeight: 1080, + pageLoadTimeout: 120000, + }, + env: { + ...process.env, + defaultAccount: process.env.default_USERNAME, + defaultPassword: process.env.default_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, }, }) From 837712b7421b48e22aac3c359a96af4b925611dc Mon Sep 17 00:00:00 2001 From: Annie Huang Date: Mon, 13 Nov 2023 22:15:39 +0800 Subject: [PATCH 02/17] Update tests: - Add public command to press enter until story end - Add public command to check status pending (cherry picked from commit c86163fd101553d56d9280b363d18b1ef80877eb) --- tests/cypress/e2e/helloworld.cy.js | 188 ++++++++++++++++++++++++++++- tests/cypress/support/commands.js | 82 +++++++++---- 2 files changed, 239 insertions(+), 31 deletions(-) diff --git a/tests/cypress/e2e/helloworld.cy.js b/tests/cypress/e2e/helloworld.cy.js index 97cffef..cbdd011 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('Complete the Game', () => { + // 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..2e58468 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -1,25 +1,57 @@ -// *********************************************** -// 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.get('a.text-text-primary', { timeout: 100000 }).contains('Log in').click() + cy.get('#username').type(username) + cy.get('#password').type(password) + cy.findByRole('button', { name: 'Log In' }).click() +}) +Cypress.Commands.add('PrepareForGame', () => { + cy.visit('/') + cy.LoginWithPassword( + Cypress.env('defaultAccount'), + Cypress.env('defaultPassword') + ) + cy.get('.xterm-screen').as('Terminaltextbox').should('be.visible') + cy.findByRole('button', { name: 'Reset' }).click() + cy.typeInCommand('clear{enter}') + 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' + ) +}) From 610eb3d2b7081e7858fa8df924cb3d3f0fe2af52 Mon Sep 17 00:00:00 2001 From: Annie Huang Date: Mon, 13 Nov 2023 22:20:10 +0800 Subject: [PATCH 03/17] update tests - having bug testing blocked by limited reset time - not yet check the playing part is fine (cherry picked from commit c510e4a54161c2f8884d3b7f63b900c1f3269762) --- quests/helloworld/answer.sh | 4 ++++ tests/cypress/e2e/helloworld.cy.js | 5 +++++ tests/cypress/support/commands.js | 4 ++++ yarn.lock | 4 +--- 4 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 quests/helloworld/answer.sh 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/tests/cypress/e2e/helloworld.cy.js b/tests/cypress/e2e/helloworld.cy.js index cbdd011..493a8ad 100644 --- a/tests/cypress/e2e/helloworld.cy.js +++ b/tests/cypress/e2e/helloworld.cy.js @@ -142,6 +142,8 @@ describe('example helloworld app', () => { cy.checkHint(1, 1) cy.getQuestInfo('✓ 輸入 `echo help` 來朗誦咒語').should('be.visible') cy.getQuestInfo('搜索卷軸').should('be.visible') + cy.getQuestInfo('✓ 輸入 `echo help` 來朗誦咒語').should('be.visible') + cy.getQuestInfo('搜索卷軸').should('be.visible') // Stage2 cy.log('Stage2') cy.typeInCommand(answerarr[1]) @@ -154,10 +156,13 @@ describe('example helloworld app', () => { cy.get('#Lbutton').should('be.visible').and('be.enabled') cy.get('#Rbutton').should('be.visible').and('be.disabled') cy.checkHint(2, 2) + cy.checkHint(2, 2) cy.get('#Lbutton').click() cy.get('#Rbutton').should('be.enabled') cy.getQuestInfo('✓ 搜索卷軸').should('be.visible') cy.getQuestInfo('查看卷軸').should('be.visible') + cy.getQuestInfo('✓ 搜索卷軸').should('be.visible') + cy.getQuestInfo('查看卷軸').should('be.visible') // Stage3 cy.log('Stage3') cy.typeInCommand(answerarr[2]) diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 2e58468..f6d6746 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -20,6 +20,10 @@ Cypress.Commands.add('PrepareForGame', () => { 'contain', 'commander:~ $' ) + cy.get('@Terminaltextbox', { timeout: 150000 }).should( + 'contain', + 'commander:~ $' + ) }) Cypress.Commands.add('typeInCommand', (command) => { cy.get('.xterm-screen', { timeout: 150000 }).type(command) diff --git a/yarn.lock b/yarn.lock index 18ab147..221eb7e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -314,9 +314,7 @@ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/parser" "^7.22.15" - "@babel/types" "^7.22.15" + regenerator-runtime "^0.14.0" "@babel/template@^7.22.5", "@babel/template@^7.3.3": version "7.22.5" From bb63057e925b6aba7c0edd1c78206fe3e78aac76 Mon Sep 17 00:00:00 2001 From: Annie Huang Date: Mon, 13 Nov 2023 22:28:53 +0800 Subject: [PATCH 04/17] update tests - having bug testing blocked by limited reset time - not yet check the playing part is fine (cherry picked from commit c510e4a54161c2f8884d3b7f63b900c1f3269762) --- tests/cypress/e2e/helloworld.cy.js | 45 ++++++++++++++++-------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/tests/cypress/e2e/helloworld.cy.js b/tests/cypress/e2e/helloworld.cy.js index 493a8ad..cee9f41 100644 --- a/tests/cypress/e2e/helloworld.cy.js +++ b/tests/cypress/e2e/helloworld.cy.js @@ -1,4 +1,5 @@ /// + export default function checkLoginUI() { cy.get('h1.text-text-primary') .should('contain', 'Linux Odyssey') @@ -7,6 +8,13 @@ export default function checkLoginUI() { .invoke('attr', 'placeholder') .should('contain', 'Password') } +export function getTerminalrowsContain(content) { + cy.get('@Terminaltextbox') + .get('.xterm-rows') + .children() + .filter(`:contains('${content}')`) +} + describe('example helloworld app', () => { describe('Login tests', () => { beforeEach(() => { @@ -48,12 +56,6 @@ describe('example helloworld app', () => { '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'), @@ -69,8 +71,6 @@ describe('example helloworld app', () => { 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!') @@ -135,20 +135,22 @@ describe('example helloworld app', () => { // Stage1 cy.log('Stage1') cy.typeInCommand(answerarr[0]) - cy.checkPending() - cy.waitUntilActive() + cy.get('input[id="currentStatus"]', { timeout: 1000000 }).should( + 'have.value', + 'active' + ) 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') - 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('input[id="currentStatus"]', { timeout: 1000000 }).should( + 'have.value', + 'active' + ) cy.get('#tree') .get('a') .should('contain', 'forgotten_scroll.txt') @@ -156,26 +158,27 @@ describe('example helloworld app', () => { cy.get('#Lbutton').should('be.visible').and('be.enabled') cy.get('#Rbutton').should('be.visible').and('be.disabled') cy.checkHint(2, 2) - cy.checkHint(2, 2) cy.get('#Lbutton').click() cy.get('#Rbutton').should('be.enabled') cy.getQuestInfo('✓ 搜索卷軸').should('be.visible') cy.getQuestInfo('查看卷軸').should('be.visible') - cy.getQuestInfo('✓ 搜索卷軸').should('be.visible') - cy.getQuestInfo('查看卷軸').should('be.visible') // Stage3 cy.log('Stage3') cy.typeInCommand(answerarr[2]) - cy.checkPending() - cy.waitUntilActive() + cy.get('input[id="currentStatus"]', { timeout: 1000000 }).should( + 'have.value', + 'active' + ) 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.get('input[id="currentStatus"]', { timeout: 1000000 }).should( + 'have.value', + 'active' + ) cy.getQuestInfo('✓ 解除封印').should('be.visible') cy.findByRole( 'heading', From 3dd0aaedb21fe971d35f5dfe52a1fbee1b60ba51 Mon Sep 17 00:00:00 2001 From: Annie Huang Date: Mon, 13 Nov 2023 22:29:56 +0800 Subject: [PATCH 05/17] remove usless code addtional information: - add loading .env file in config (cherry picked from commit 5d7c922fa6c463fb2bd4187d2a7effc63aabf04d) --- tests/cypress/e2e/helloworld.cy.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/cypress/e2e/helloworld.cy.js b/tests/cypress/e2e/helloworld.cy.js index cee9f41..d6b097e 100644 --- a/tests/cypress/e2e/helloworld.cy.js +++ b/tests/cypress/e2e/helloworld.cy.js @@ -8,13 +8,6 @@ export default function checkLoginUI() { .invoke('attr', 'placeholder') .should('contain', 'Password') } -export function getTerminalrowsContain(content) { - cy.get('@Terminaltextbox') - .get('.xterm-rows') - .children() - .filter(`:contains('${content}')`) -} - describe('example helloworld app', () => { describe('Login tests', () => { beforeEach(() => { From 62fe65d53a4e044e3f7e0ad1255c9aed8d24cee8 Mon Sep 17 00:00:00 2001 From: Annie Huang Date: Mon, 13 Nov 2023 22:30:22 +0800 Subject: [PATCH 06/17] Update tests: - Add public command to press enter until story end - Add public command to check status pending (cherry picked from commit c86163fd101553d56d9280b363d18b1ef80877eb) --- tests/cypress/e2e/helloworld.cy.js | 33 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/tests/cypress/e2e/helloworld.cy.js b/tests/cypress/e2e/helloworld.cy.js index d6b097e..cbdd011 100644 --- a/tests/cypress/e2e/helloworld.cy.js +++ b/tests/cypress/e2e/helloworld.cy.js @@ -1,5 +1,4 @@ /// - export default function checkLoginUI() { cy.get('h1.text-text-primary') .should('contain', 'Linux Odyssey') @@ -49,6 +48,12 @@ describe('example helloworld app', () => { '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'), @@ -64,6 +69,8 @@ describe('example helloworld app', () => { 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!') @@ -128,10 +135,8 @@ describe('example helloworld app', () => { // Stage1 cy.log('Stage1') cy.typeInCommand(answerarr[0]) - cy.get('input[id="currentStatus"]', { timeout: 1000000 }).should( - 'have.value', - 'active' - ) + 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) @@ -140,10 +145,8 @@ describe('example helloworld app', () => { // Stage2 cy.log('Stage2') cy.typeInCommand(answerarr[1]) - cy.get('input[id="currentStatus"]', { timeout: 1000000 }).should( - 'have.value', - 'active' - ) + cy.checkPending() + cy.waitUntilActive() cy.get('#tree') .get('a') .should('contain', 'forgotten_scroll.txt') @@ -158,20 +161,16 @@ describe('example helloworld app', () => { // Stage3 cy.log('Stage3') cy.typeInCommand(answerarr[2]) - cy.get('input[id="currentStatus"]', { timeout: 1000000 }).should( - 'have.value', - 'active' - ) + 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.get('input[id="currentStatus"]', { timeout: 1000000 }).should( - 'have.value', - 'active' - ) + cy.checkPending() + cy.waitUntilActive() cy.getQuestInfo('✓ 解除封印').should('be.visible') cy.findByRole( 'heading', From f9914b80954b015ecc995975153b8639b52c9388 Mon Sep 17 00:00:00 2001 From: Annie Huang Date: Mon, 13 Nov 2023 23:08:29 +0800 Subject: [PATCH 07/17] renew config and package --- tests/cypress.config.js | 1 + tests/package.json | 8 +- yarn.lock | 374 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 372 insertions(+), 11 deletions(-) diff --git a/tests/cypress.config.js b/tests/cypress.config.js index 98002f1..2642790 100644 --- a/tests/cypress.config.js +++ b/tests/cypress.config.js @@ -1,5 +1,6 @@ const { defineConfig } = require('cypress') const process = require('process') +require('dotenv').config() const getConfig = (key, defaultValue) => { return process.env[key] || defaultValue diff --git a/tests/package.json b/tests/package.json index af8e1fa..2f71755 100644 --- a/tests/package.json +++ b/tests/package.json @@ -8,6 +8,12 @@ "test": "cypress run" }, "dependencies": { - "cypress": "^13.2.0" + "cypress": "^13.2.0", + "dotenv": "^16.3.1", + "save": "^2.9.0" + }, + "devDependencies": { + "@testing-library/cypress": "^10.0.1", + "cypress": "^13.5.0" } } diff --git a/yarn.lock b/yarn.lock index 221eb7e..045b82c 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,12 +314,21 @@ 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" integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== dependencies: - regenerator-runtime "^0.14.0" + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" "@babel/template@^7.22.5", "@babel/template@^7.3.3": version "7.22.5" @@ -350,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== @@ -946,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" @@ -1334,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" @@ -1410,6 +1467,11 @@ async@^3.2.0: resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== +async@^3.2.2: + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -1717,6 +1779,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" @@ -2025,10 +2096,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" @@ -2112,6 +2183,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" @@ -2140,6 +2235,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" @@ -2231,6 +2335,21 @@ 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== + +duplexer@^0.1.1, duplexer@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" @@ -2381,6 +2500,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" @@ -2652,6 +2786,19 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== +event-stream@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-4.0.1.tgz#4092808ec995d0dd75ea4580c1df6a74db2cde65" + integrity sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA== + dependencies: + duplexer "^0.1.1" + from "^0.1.7" + map-stream "0.0.7" + pause-stream "^0.0.11" + split "^1.0.1" + stream-combiner "^0.2.2" + through "^2.3.8" + eventemitter2@6.4.7: version "6.4.7" resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d" @@ -2998,6 +3145,11 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== +from@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g== + fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" @@ -3035,6 +3187,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" @@ -3085,6 +3242,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" @@ -3286,6 +3453,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" @@ -3400,6 +3574,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" @@ -3419,6 +3602,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" @@ -3474,7 +3665,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== @@ -3528,6 +3719,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" @@ -3558,6 +3754,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" @@ -3610,6 +3811,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" @@ -3617,6 +3823,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" @@ -3624,6 +3838,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" @@ -4251,6 +4470,11 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash.assign@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" + integrity sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw== + lodash.includes@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" @@ -4343,6 +4567,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" @@ -4364,6 +4593,11 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +map-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.0.7.tgz#8a1f07896d82b10926bd3744a2420009f88974a8" + integrity sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ== + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -4429,6 +4663,11 @@ mimic-fn@^4.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== +mingo@^6.1.0: + version "6.4.9" + resolved "https://registry.yarnpkg.com/mingo/-/mingo-6.4.9.tgz#715ad1487687259db04623b64a7ba63b564b5ddd" + integrity sha512-jsOiIEJ19UtTaWjNFN4CnuKWqELm2sINZcHqI/nmjezMvXthBIQOftJrlT85NuIDoSFc0vrbjD+GnrzEgYnPbA== + minimalistic-assert@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -4727,6 +4966,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" @@ -4982,6 +5229,13 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== +pause-stream@^0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + integrity sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A== + dependencies: + through "~2.3" + pause@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d" @@ -5099,6 +5353,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" @@ -5211,6 +5474,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" @@ -5239,6 +5507,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" @@ -5248,6 +5521,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" @@ -5375,6 +5657,16 @@ saslprep@^1.0.3: dependencies: sparse-bitfield "^3.0.3" +save@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/save/-/save-2.9.0.tgz#6659375fadeaf58e4abc6a90ec6b0fb2e2232e0f" + integrity sha512-eg8+g8CjvehE/2C6EbLdtK1pINVD27pcJLj4M9PjWWhoeha/y5bWf4dp/0RF+OzbKTcG1bae9qi3PAqiR8CJTg== + dependencies: + async "^3.2.2" + event-stream "^4.0.1" + lodash.assign "^4.2.0" + mingo "^6.1.0" + semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" @@ -5421,6 +5713,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" @@ -5583,6 +5894,13 @@ split-ca@^1.0.1: resolved "https://registry.yarnpkg.com/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6" integrity sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ== +split@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" + integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== + dependencies: + through "2" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -5626,6 +5944,21 @@ 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" + +stream-combiner@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.2.2.tgz#aec8cbac177b56b6f4fa479ced8c1912cee52858" + integrity sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ== + dependencies: + duplexer "~0.1.1" + through "~2.3.4" + string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -5878,7 +6211,7 @@ throttleit@^1.0.0: resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" integrity sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g== -through@^2.3.8: +through@2, through@^2.3.8, through@~2.3, through@~2.3.4: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== @@ -6237,6 +6570,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" @@ -6249,6 +6592,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" From 5a90b26af688a04ff8e5adcc097781c1faa6510c Mon Sep 17 00:00:00 2001 From: Annie Huang Date: Thu, 16 Nov 2023 15:01:36 +0800 Subject: [PATCH 08/17] add new command(not yet finish loop enter command) --- tests/cypress/e2e/helloworld.cy.js | 2 +- tests/cypress/support/commands.js | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/cypress/e2e/helloworld.cy.js b/tests/cypress/e2e/helloworld.cy.js index cbdd011..5894660 100644 --- a/tests/cypress/e2e/helloworld.cy.js +++ b/tests/cypress/e2e/helloworld.cy.js @@ -128,7 +128,7 @@ describe('example helloworld app', () => { .should('contain', 'zsh: command not found: 12345') .and('contain', '12345') }) - it('Complete the Game', () => { + it.only('Complete the Game(relating UI)', () => { // get answersheet cy.get('@answers').then((answers) => { const answerarr = answers.split('\n') diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index f6d6746..fa0f4b6 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -59,3 +59,6 @@ Cypress.Commands.add('checkPending', () => { 'pending' ) }) +Cypress.Commands.add('Complete the Stage (only command)', () => { + cy.log('') +}) From 1f77328362ce595bb1e4e5ce7eace862eea0bdba Mon Sep 17 00:00:00 2001 From: "Lin, Hung-Shin" Date: Fri, 17 Nov 2023 10:57:57 +0800 Subject: [PATCH 09/17] feat: add testing setup in backend --- docker-compose.testing.yml | 12 ++- docker-compose.yml | 3 + package.json | 1 + server/src/api/controllers/authController.js | 10 +-- .../src/api/controllers/commandController.js | 2 +- .../src/api/controllers/sessionController.js | 2 +- server/src/config.js | 5 ++ server/src/index.js | 5 ++ server/src/{game => models}/sessionManager.js | 2 +- server/src/models/userManager.js | 16 ++++ server/src/utils/test.js | 22 ++++++ tests/Dockerfile | 9 +++ tests/cypress.config.js | 15 +++- tests/package.json | 11 +-- yarn.lock | 77 +------------------ 15 files changed, 92 insertions(+), 100 deletions(-) rename server/src/{game => models}/sessionManager.js (98%) create mode 100644 server/src/models/userManager.js create mode 100644 server/src/utils/test.js create mode 100644 tests/Dockerfile diff --git a/docker-compose.testing.yml b/docker-compose.testing.yml index b0c2cca..c1d2f00 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,9 @@ services: - GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET} - SURVEY_URL=${SURVEY_URL} - BUG_REPORT_URL=${BUG_REPORT_URL} + - TESTING=true + - TESTING_USERNAME=alex + - TESTING_PASSWORD=Alex1234 db-test: image: mongo diff --git a/docker-compose.yml b/docker-compose.yml index b2b3e9f..ac3b400 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 e7724c5..b117891 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/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 75f43d8..e9b5da0 100644 --- a/server/src/config.js +++ b/server/src/config.js @@ -40,6 +40,11 @@ const config = { trustedProxies: getTrustProxies('TRUSTED_PROXIES'), surveyUrl: getUrl('SURVEY_URL', ''), bugReportUrl: getUrl('BUG_REPORT_URL', ''), + testing: { + enabled: process.env.TESTING === 'true', + username: get('TESTING_USERNAME', ''), + password: get('TESTING_PASSWORD', ''), + }, } config.baseUrl = get('BASE_URL', `http://${config.host}:${config.port}`) diff --git a/server/src/index.js b/server/src/index.js index 52c2d88..e22f9cf 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/test.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/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 19e79c8..b846dfb 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/test.js b/server/src/utils/test.js new file mode 100644 index 0000000..2c97278 --- /dev/null +++ b/server/src/utils/test.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..da38a2c --- /dev/null +++ b/tests/Dockerfile @@ -0,0 +1,9 @@ +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 tests ./tests +WORKDIR /e2e/tests \ No newline at end of file diff --git a/tests/cypress.config.js b/tests/cypress.config.js index 2642790..9057bb2 100644 --- a/tests/cypress.config.js +++ b/tests/cypress.config.js @@ -1,11 +1,19 @@ const { defineConfig } = require('cypress') const process = require('process') -require('dotenv').config() +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'), @@ -14,9 +22,8 @@ module.exports = defineConfig({ pageLoadTimeout: 120000, }, env: { - ...process.env, - defaultAccount: process.env.default_USERNAME, - defaultPassword: process.env.default_PASSWORD, + 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, diff --git a/tests/package.json b/tests/package.json index 2f71755..be6c38b 100644 --- a/tests/package.json +++ b/tests/package.json @@ -5,15 +5,12 @@ "main": "index.js", "license": "MIT", "scripts": { - "test": "cypress run" + "test": "cypress run", + "test:open": "cypress open" }, "dependencies": { - "cypress": "^13.2.0", - "dotenv": "^16.3.1", - "save": "^2.9.0" - }, - "devDependencies": { + "cypress": "^13.5.0", "@testing-library/cypress": "^10.0.1", - "cypress": "^13.5.0" + "dotenv": "^16.3.1" } } diff --git a/yarn.lock b/yarn.lock index d25b117..19a6782 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1467,11 +1467,6 @@ async@^3.2.0: resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== -async@^3.2.2: - version "3.2.5" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" - integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -2345,11 +2340,6 @@ dotenv@^16.3.1: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== -duplexer@^0.1.1, duplexer@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" - integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== - eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" @@ -2786,19 +2776,6 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== -event-stream@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-4.0.1.tgz#4092808ec995d0dd75ea4580c1df6a74db2cde65" - integrity sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA== - dependencies: - duplexer "^0.1.1" - from "^0.1.7" - map-stream "0.0.7" - pause-stream "^0.0.11" - split "^1.0.1" - stream-combiner "^0.2.2" - through "^2.3.8" - eventemitter2@6.4.7: version "6.4.7" resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d" @@ -3145,11 +3122,6 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== -from@^0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" - integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g== - fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" @@ -4470,11 +4442,6 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash.assign@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" - integrity sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw== - lodash.includes@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" @@ -4593,11 +4560,6 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" -map-stream@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.0.7.tgz#8a1f07896d82b10926bd3744a2420009f88974a8" - integrity sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ== - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -4663,11 +4625,6 @@ mimic-fn@^4.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== -mingo@^6.1.0: - version "6.4.9" - resolved "https://registry.yarnpkg.com/mingo/-/mingo-6.4.9.tgz#715ad1487687259db04623b64a7ba63b564b5ddd" - integrity sha512-jsOiIEJ19UtTaWjNFN4CnuKWqELm2sINZcHqI/nmjezMvXthBIQOftJrlT85NuIDoSFc0vrbjD+GnrzEgYnPbA== - minimalistic-assert@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -5229,13 +5186,6 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== -pause-stream@^0.0.11: - version "0.0.11" - resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" - integrity sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A== - dependencies: - through "~2.3" - pause@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d" @@ -5657,16 +5607,6 @@ saslprep@^1.0.3: dependencies: sparse-bitfield "^3.0.3" -save@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/save/-/save-2.9.0.tgz#6659375fadeaf58e4abc6a90ec6b0fb2e2232e0f" - integrity sha512-eg8+g8CjvehE/2C6EbLdtK1pINVD27pcJLj4M9PjWWhoeha/y5bWf4dp/0RF+OzbKTcG1bae9qi3PAqiR8CJTg== - dependencies: - async "^3.2.2" - event-stream "^4.0.1" - lodash.assign "^4.2.0" - mingo "^6.1.0" - semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" @@ -5894,13 +5834,6 @@ split-ca@^1.0.1: resolved "https://registry.yarnpkg.com/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6" integrity sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ== -split@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" - integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== - dependencies: - through "2" - sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -5951,14 +5884,6 @@ stop-iteration-iterator@^1.0.0: dependencies: internal-slot "^1.0.4" -stream-combiner@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.2.2.tgz#aec8cbac177b56b6f4fa479ced8c1912cee52858" - integrity sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ== - dependencies: - duplexer "~0.1.1" - through "~2.3.4" - string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -6211,7 +6136,7 @@ throttleit@^1.0.0: resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" integrity sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g== -through@2, through@^2.3.8, through@~2.3, through@~2.3.4: +through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== From d0c01fffa8a867ce9c60510e1c78a436ef5daba4 Mon Sep 17 00:00:00 2001 From: "Lin, Hung-Shin" Date: Fri, 17 Nov 2023 11:18:14 +0800 Subject: [PATCH 10/17] fix: enhance error handling in frontend --- app/src/store/session.js | 18 +++++++++++++++--- app/src/utils/errors.js | 18 ++++++++++++++---- app/src/views/GamePage.vue | 13 ++++++++++++- 3 files changed, 41 insertions(+), 8 deletions(-) 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/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) } }) From ca057a657b4564656add3e9e8e4565f4efbfb902 Mon Sep 17 00:00:00 2001 From: "Lin, Hung-Shin" Date: Fri, 17 Nov 2023 11:27:48 +0800 Subject: [PATCH 11/17] feat: disable rate limits when testing --- server/src/config.js | 10 +++++----- server/src/middleware/rateLimiter.js | 14 +++++++++++--- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/server/src/config.js b/server/src/config.js index e9b5da0..68da085 100644 --- a/server/src/config.js +++ b/server/src/config.js @@ -40,13 +40,13 @@ const config = { trustedProxies: getTrustProxies('TRUSTED_PROXIES'), surveyUrl: getUrl('SURVEY_URL', ''), bugReportUrl: getUrl('BUG_REPORT_URL', ''), - testing: { - enabled: process.env.TESTING === 'true', - username: get('TESTING_USERNAME', ''), - password: get('TESTING_PASSWORD', ''), - }, } 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/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) => { From 9193b9518a6d68894f7bf42ddf8fde8b23e654ca Mon Sep 17 00:00:00 2001 From: "Lin, Hung-Shin" Date: Fri, 17 Nov 2023 11:41:09 +0800 Subject: [PATCH 12/17] fix: handle frontend error --- app/src/utils/socket.js | 4 ++++ 1 file changed, 4 insertions(+) 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) } From 387f577ae1ce7a350842586f58ea2786a4fcbf05 Mon Sep 17 00:00:00 2001 From: "Lin, Hung-Shin" Date: Fri, 17 Nov 2023 11:41:33 +0800 Subject: [PATCH 13/17] fix: rename setupTest --- server/src/index.js | 2 +- server/src/utils/{test.js => setupTest.js} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename server/src/utils/{test.js => setupTest.js} (100%) diff --git a/server/src/index.js b/server/src/index.js index e22f9cf..aa2358d 100644 --- a/server/src/index.js +++ b/server/src/index.js @@ -15,7 +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/test.js' +import setupTest from './utils/setupTest.js' async function main() { if (!config.secret) { diff --git a/server/src/utils/test.js b/server/src/utils/setupTest.js similarity index 100% rename from server/src/utils/test.js rename to server/src/utils/setupTest.js From e6de58d2ee41304544236444f651fb427cd41d6a Mon Sep 17 00:00:00 2001 From: "Lin, Hung-Shin" Date: Fri, 17 Nov 2023 11:41:38 +0800 Subject: [PATCH 14/17] fix: include quests data in cypress image --- tests/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Dockerfile b/tests/Dockerfile index da38a2c..0e35d91 100644 --- a/tests/Dockerfile +++ b/tests/Dockerfile @@ -5,5 +5,6 @@ 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 From 8827446b711067772097048224ade213db7f7e8f Mon Sep 17 00:00:00 2001 From: "Lin, Hung-Shin" Date: Fri, 17 Nov 2023 11:50:04 +0800 Subject: [PATCH 15/17] fix: add longer timeout for getting xterm --- tests/cypress/support/commands.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index fa0f4b6..8d0aef7 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -13,7 +13,9 @@ Cypress.Commands.add('PrepareForGame', () => { Cypress.env('defaultAccount'), Cypress.env('defaultPassword') ) - cy.get('.xterm-screen').as('Terminaltextbox').should('be.visible') + cy.get('.xterm-screen', { timeout: 150000 }) + .as('Terminaltextbox') + .should('be.visible') cy.findByRole('button', { name: 'Reset' }).click() cy.typeInCommand('clear{enter}') cy.get('@Terminaltextbox', { timeout: 150000 }).should( From 535fef247fde8ea9cf3cb549cd15a4e6d78af742 Mon Sep 17 00:00:00 2001 From: wancat Date: Fri, 17 Nov 2023 13:46:42 +0800 Subject: [PATCH 16/17] fix: fix login failure by NODE_ENV=developement --- docker-compose.testing.yml | 1 + tests/cypress/support/commands.js | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docker-compose.testing.yml b/docker-compose.testing.yml index c1d2f00..de665b2 100644 --- a/docker-compose.testing.yml +++ b/docker-compose.testing.yml @@ -52,6 +52,7 @@ 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 diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 8d0aef7..fa1d735 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -2,10 +2,11 @@ import '@testing-library/cypress/add-commands' Cypress.Commands.add('LoginWithPassword', (username, password) => { cy.clearAllCookies({ domain: null }) - cy.get('a.text-text-primary', { timeout: 100000 }).contains('Log in').click() + 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('/') @@ -13,7 +14,8 @@ Cypress.Commands.add('PrepareForGame', () => { Cypress.env('defaultAccount'), Cypress.env('defaultPassword') ) - cy.get('.xterm-screen', { timeout: 150000 }) + cy.visit('/game/helloworld') + cy.get('.xterm-screen', { timeout: 10000 }) .as('Terminaltextbox') .should('be.visible') cy.findByRole('button', { name: 'Reset' }).click() From 0e3b3cca11f7cc089eca095b4f30bc1808847fc3 Mon Sep 17 00:00:00 2001 From: wancat Date: Fri, 17 Nov 2023 13:57:56 +0800 Subject: [PATCH 17/17] fix: remove tear down step --- .github/workflows/ci_cd.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 73fbcb8..1dc32c6 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