mirror of
https://github.com/linux-odyssey/linux-odyssey.git
synced 2026-11-06 03:20:13 +08:00
Feat automation test (#63)
Add cypress automation tests - Verify Login and Register UI - Verify Game Starting UI - Verify Game Play - Add global commands: Login with Password, Prepare for Game, Type Command, Wait until active, Check pending - Add .sh file for entering command - Add .env loading in config - Removed login using Google and GitHub
This commit is contained in:
24 files changed
+702
-68
No files matched your search
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
+14
-4
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,10 @@ class SocketWrapper {
|
||||
}
|
||||
|
||||
emit(event, data) {
|
||||
if (!this.socket) {
|
||||
console.warn('Socket is not connected!')
|
||||
return
|
||||
}
|
||||
this.socket.emit(event, data)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
echo help{enter}
|
||||
ls {enter}
|
||||
cat forgotten_scroll.txt{enter}
|
||||
echo Hello World!{enter}
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -43,5 +43,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
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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({
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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')
|
||||
}
|
||||
@@ -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
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
@@ -1,10 +1,186 @@
|
||||
/// <reference types="cypress" />
|
||||
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')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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('')
|
||||
})
|
||||
+5
-2
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
Reference in new issue
Block a user