mirror of
https://github.com/linux-odyssey/linux-odyssey.git
synced 2026-11-05 19:20:12 +08:00
Merge remote-tracking branch 'origin/main' into feat-landing-page
This commit is contained in:
commit
6aed5789a5
13 files changed
+131
-146
No files matched your search
@@ -26,53 +26,65 @@ const right = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full flex items-center p-2">
|
<div class="h-full flex flex-col">
|
||||||
<font-awesome-icon
|
<!-- Hint Header -->
|
||||||
:icon="['far', 'lightbulb']"
|
<div class="flex items-center p-2 h-10">
|
||||||
class="text-yellow-200 p-2 content-center"
|
<font-awesome-icon
|
||||||
/>
|
:icon="['far', 'lightbulb']"
|
||||||
<h1 class="inline text-text w-1/2 font-xl p-2 m-1">
|
class="text-yellow-200 p-2 content-center"
|
||||||
<!-- Hint -->
|
/>
|
||||||
提示
|
<h1 class="inline text-text w-1/2 font-xl p-2 m-1">
|
||||||
</h1>
|
<!-- Hint -->
|
||||||
<div v-if="current !== -1" class="flex w-full justify-end items-end">
|
提示
|
||||||
<button
|
</h1>
|
||||||
class="p-2 m-1 w-1/8"
|
<div v-if="current !== -1" class="flex w-full justify-end items-end">
|
||||||
id="Lbutton"
|
<button
|
||||||
@click="left"
|
class="p-2 m-1 w-1/8"
|
||||||
:disabled="current <= 0"
|
id="Lbutton"
|
||||||
>
|
@click="left"
|
||||||
<font-awesome-icon
|
:disabled="current <= 0"
|
||||||
:icon="['fas', 'arrow-right']"
|
>
|
||||||
class="text-text"
|
<font-awesome-icon
|
||||||
rotation="180"
|
:icon="['fas', 'arrow-right']"
|
||||||
/>
|
class="text-text"
|
||||||
</button>
|
rotation="180"
|
||||||
<p class="inline text-text font-xl w-1/14 p-2 m-1">{{ current + 1 }}</p>
|
/>
|
||||||
<p class="inline text-text font-xl w-1/14 p-2 m-1">/</p>
|
</button>
|
||||||
<p class="inline text-text font-xl w-1/14 p-2 m-1">
|
<p class="inline text-text font-xl w-1/14 p-2 m-1">{{ current + 1 }}</p>
|
||||||
{{ sessionStore.session.hints.length }}
|
<p class="inline text-text font-xl w-1/14 p-2 m-1">/</p>
|
||||||
</p>
|
<p class="inline text-text font-xl w-1/14 p-2 m-1">
|
||||||
<button
|
{{ sessionStore.session.hints.length }}
|
||||||
class="p-2 m-1 w-1/8"
|
</p>
|
||||||
id="Rbutton"
|
<button
|
||||||
@click="right"
|
class="p-2 m-1 w-1/8"
|
||||||
:disabled="current === sessionStore.session.hints.length - 1"
|
id="Rbutton"
|
||||||
>
|
@click="right"
|
||||||
<font-awesome-icon :icon="['fas', 'arrow-right']" class="text-text" />
|
:disabled="current === sessionStore.session.hints.length - 1"
|
||||||
</button>
|
>
|
||||||
|
<font-awesome-icon :icon="['fas', 'arrow-right']" class="text-text" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="hint" class="bg-bg p-8 overflow-y-auto">
|
||||||
|
<ul>
|
||||||
|
<li
|
||||||
|
v-for="response in sessionStore.session.responses[current]"
|
||||||
|
:key="response"
|
||||||
|
class="text-text font-xl whitespace-pre-wrap"
|
||||||
|
>
|
||||||
|
<div v-for="content in response.content" :key="content">
|
||||||
|
<MarkdownText :content="content" />
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
v-for="hint in sessionStore.session.hints[current]"
|
||||||
|
:key="hint"
|
||||||
|
class="text-text-primary font-xl whitespace-pre-wrap"
|
||||||
|
>
|
||||||
|
<MarkdownText :content="hint" />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<br />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="hint" class="bg-bg flex flex-wrap p-8">
|
|
||||||
<ul>
|
|
||||||
<li
|
|
||||||
v-for="hint in sessionStore.session.hints[current]"
|
|
||||||
:key="hint"
|
|
||||||
class="text-text font-xl whitespace-pre-wrap"
|
|
||||||
>
|
|
||||||
<MarkdownText :content="hint" />
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<br />
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
+10
-18
@@ -20,6 +20,7 @@ function newSession() {
|
|||||||
pwd: '',
|
pwd: '',
|
||||||
hints: [],
|
hints: [],
|
||||||
tasks: [],
|
tasks: [],
|
||||||
|
responses: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +42,7 @@ function setQuest(questId) {
|
|||||||
|
|
||||||
async function setSession(session) {
|
async function setSession(session) {
|
||||||
try {
|
try {
|
||||||
|
console.log('Setting session...', session)
|
||||||
store.session = session
|
store.session = session
|
||||||
store.session.graph = new FileGraph(session.graph)
|
store.session.graph = new FileGraph(session.graph)
|
||||||
term.reset()
|
term.reset()
|
||||||
@@ -85,16 +87,12 @@ function updateGraph(event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateHints(hints) {
|
function newResponse(response) {
|
||||||
store.session.hints.push(hints)
|
console.log('newResponse', response, 'session', store.session)
|
||||||
}
|
store.session.responses.push(response.responses)
|
||||||
|
store.session.hints.push(response.hints)
|
||||||
function updateStatus(status) {
|
store.session.tasks = response.tasks
|
||||||
store.session.status = status
|
store.session.status = response.status
|
||||||
}
|
|
||||||
|
|
||||||
function setTasks(tasks) {
|
|
||||||
store.session.tasks = tasks
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useTerminal() {
|
export function useTerminal() {
|
||||||
@@ -135,14 +133,8 @@ function setup() {
|
|||||||
socket.on('graph', (event) => {
|
socket.on('graph', (event) => {
|
||||||
updateGraph(event)
|
updateGraph(event)
|
||||||
})
|
})
|
||||||
socket.on('hints', (event) => {
|
socket.on('response', (response) => {
|
||||||
updateHints(event)
|
newResponse(response)
|
||||||
})
|
|
||||||
socket.on('tasks', (tasks) => {
|
|
||||||
setTasks(tasks)
|
|
||||||
})
|
|
||||||
socket.on('status', (event) => {
|
|
||||||
updateStatus(event)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ onMounted(async () => {
|
|||||||
<!-- main -->
|
<!-- main -->
|
||||||
<div id="main" class="h-[94vh] w-full flex p-3 space-x-3">
|
<div id="main" class="h-[94vh] w-full flex p-3 space-x-3">
|
||||||
<!-- Topic and Command List -->
|
<!-- Topic and Command List -->
|
||||||
<div class="bg-bg-secondary h-full w-1/3 rounded-lg">
|
<div class="bg-bg-secondary h-full w-1/4 rounded-lg">
|
||||||
<section id="quest" class="h-full p-3 overflow-auto">
|
<section id="quest" class="h-full p-3 overflow-auto">
|
||||||
<QuestPart />
|
<QuestPart />
|
||||||
</section>
|
</section>
|
||||||
@@ -71,17 +71,17 @@ onMounted(async () => {
|
|||||||
</section> -->
|
</section> -->
|
||||||
</div>
|
</div>
|
||||||
<!-- Terminal and Hint -->
|
<!-- Terminal and Hint -->
|
||||||
<div class="bg-bg h-full w-1/3 rounded-lg">
|
<div class="bg-bg h-full w-1/2 rounded-lg">
|
||||||
<section id="terminal" class="h-3/5 overflow-hidden">
|
<section id="hint" class="h-3/5">
|
||||||
<TerminalPart />
|
<HintPart />
|
||||||
</section>
|
</section>
|
||||||
<hr class="border-border border" />
|
<hr class="border-border border" />
|
||||||
<section id="hint" class="h-2/5">
|
<section id="terminal" class="h-2/5 overflow-hidden">
|
||||||
<HintPart />
|
<TerminalPart />
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<!-- Visualization -->
|
<!-- Visualization -->
|
||||||
<div class="h-full w-1/3">
|
<div class="h-full w-1/4">
|
||||||
<div class="bg-border rounded-xl h-[90%] p-2">
|
<div class="bg-border rounded-xl h-[90%] p-2">
|
||||||
<div class="bg-bg rounded-lg h-full p-2">
|
<div class="bg-bg rounded-lg h-full p-2">
|
||||||
<section id="visualization" class="bg-bg-primary h-full rounded-md">
|
<section id="visualization" class="bg-bg-primary h-full rounded-md">
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { model, Schema, Types } from 'mongoose'
|
import { model, Schema, Types } from 'mongoose'
|
||||||
|
|
||||||
|
import { IResponse, responseSchema } from './response.js'
|
||||||
|
|
||||||
export interface INode {
|
export interface INode {
|
||||||
path: string
|
path: string
|
||||||
type: string
|
type: string
|
||||||
@@ -46,13 +48,14 @@ export interface ISession {
|
|||||||
user: Types.ObjectId
|
user: Types.ObjectId
|
||||||
quest: string
|
quest: string
|
||||||
containerId: string | null
|
containerId: string | null
|
||||||
status: string
|
status: 'active' | 'finished' | 'inactive'
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
finishedAt?: Date
|
finishedAt?: Date
|
||||||
lastActivityAt: Date
|
lastActivityAt: Date
|
||||||
tasks: ITask[]
|
tasks: ITask[]
|
||||||
hints: string[][]
|
hints: string[][]
|
||||||
|
responses: IResponse[][]
|
||||||
graph: INode
|
graph: INode
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,6 +88,11 @@ export const Session = model<ISession>(
|
|||||||
},
|
},
|
||||||
tasks: [taskSchema],
|
tasks: [taskSchema],
|
||||||
hints: [[String]],
|
hints: [[String]],
|
||||||
|
responses: {
|
||||||
|
type: [[responseSchema]],
|
||||||
|
required: true,
|
||||||
|
default: [],
|
||||||
|
},
|
||||||
graph: {
|
graph: {
|
||||||
type: nodeSchema,
|
type: nodeSchema,
|
||||||
default: {
|
default: {
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import { pushToSession } from '../socket.js'
|
|||||||
import { finishSession } from '../../models/sessionManager.js'
|
import { finishSession } from '../../models/sessionManager.js'
|
||||||
import { asyncHandler } from '../../middleware/error.js'
|
import { asyncHandler } from '../../middleware/error.js'
|
||||||
|
|
||||||
const commandCompleteCallbacks = new Map()
|
|
||||||
|
|
||||||
export const newCommand = asyncHandler(async (req: Request, res: Response) => {
|
export const newCommand = asyncHandler(async (req: Request, res: Response) => {
|
||||||
const { command, pwd, output, error, params } = matchedData(req)
|
const { command, pwd, output, error, params } = matchedData(req)
|
||||||
|
|
||||||
@@ -51,42 +49,14 @@ export const newCommand = asyncHandler(async (req: Request, res: Response) => {
|
|||||||
c.stage = response.stage
|
c.stage = response.stage
|
||||||
await c.save()
|
await c.save()
|
||||||
|
|
||||||
if (response.stage === 'END') {
|
session.responses.push(response.responses)
|
||||||
|
|
||||||
|
if ((session.status as string) === 'finished') {
|
||||||
await finishSession(session)
|
await finishSession(session)
|
||||||
}
|
}
|
||||||
commandCompleteCallbacks.set(session.id, response.callback)
|
pushToSession(session.id, 'response', response)
|
||||||
pushToSession(session.id, 'status', 'pending')
|
|
||||||
res.status(201).json(response)
|
|
||||||
} else {
|
|
||||||
res.status(200).end()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res.status(200).end()
|
||||||
await session.save()
|
await session.save()
|
||||||
})
|
})
|
||||||
|
|
||||||
export const completedCommand = asyncHandler(
|
|
||||||
async (req: Request, res: Response) => {
|
|
||||||
const { sessionId } = req.user as any
|
|
||||||
|
|
||||||
const session = await Session.findById(sessionId)
|
|
||||||
|
|
||||||
if (!session) {
|
|
||||||
res.status(404).json({ message: 'session not found' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (session.status === 'inactive') {
|
|
||||||
res.status(400).json({ message: 'session is not active' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const callback = commandCompleteCallbacks.get(session.id)
|
|
||||||
if (!callback) {
|
|
||||||
res.status(200).json({ message: 'no callback found' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
callback()
|
|
||||||
commandCompleteCallbacks.delete(session.id)
|
|
||||||
res.json({ message: 'ok' })
|
|
||||||
}
|
|
||||||
)
|
|
||||||
@@ -25,6 +25,7 @@ function sessionDetail(session: ISession) {
|
|||||||
...sessionSummary(session),
|
...sessionSummary(session),
|
||||||
hints: session.hints,
|
hints: session.hints,
|
||||||
tasks: session.tasks,
|
tasks: session.tasks,
|
||||||
|
responses: session.responses,
|
||||||
graph: session.graph,
|
graph: session.graph,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,6 +56,7 @@ export const createSession = asyncHandler(
|
|||||||
export const getSessionById = asyncHandler(
|
export const getSessionById = asyncHandler(
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { sessionId } = matchedData(req)
|
const { sessionId } = matchedData(req)
|
||||||
|
console.log(sessionId)
|
||||||
const session = await Session.findOne({
|
const session = await Session.findOne({
|
||||||
_id: sessionId,
|
_id: sessionId,
|
||||||
user: (req.user as Express.ExistingUser).id,
|
user: (req.user as Express.ExistingUser).id,
|
||||||
|
|||||||
@@ -5,6 +5,5 @@ import { newCommandValidator } from '../validators/commandValidator.js'
|
|||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
router.post('/', newCommandValidator, commandController.newCommand)
|
router.post('/', newCommandValidator, commandController.newCommand)
|
||||||
router.post('/completed', commandController.completedCommand)
|
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
@@ -22,6 +22,10 @@ router.post(
|
|||||||
activeSessionValidator,
|
activeSessionValidator,
|
||||||
sessionController.getOrCreateSession
|
sessionController.getOrCreateSession
|
||||||
)
|
)
|
||||||
router.get('/:id', sessionDetailValidator, sessionController.getSessionById)
|
router.get(
|
||||||
|
'/:sessionId',
|
||||||
|
sessionDetailValidator,
|
||||||
|
sessionController.getSessionById
|
||||||
|
)
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
@@ -9,6 +9,6 @@ export const checkSessionId = () =>
|
|||||||
check('sessionId').notEmpty().isString().isMongoId()
|
check('sessionId').notEmpty().isString().isMongoId()
|
||||||
|
|
||||||
export const sessionListValidator = [checkQuestId(), checkSessionStatus()]
|
export const sessionListValidator = [checkQuestId(), checkSessionStatus()]
|
||||||
export const sessionDetailValidator = [checkSessionId(), noError]
|
export const sessionDetailValidator = [checkSessionId()]
|
||||||
export const createSessionValidator = [checkQuestId(), noError]
|
export const createSessionValidator = [checkQuestId(), noError]
|
||||||
export const activeSessionValidator = [checkQuestId(), noError]
|
export const activeSessionValidator = [checkQuestId(), noError]
|
||||||
@@ -8,12 +8,12 @@ import type {
|
|||||||
IStage,
|
IStage,
|
||||||
ITask,
|
ITask,
|
||||||
} from '@linux-odyssey/models'
|
} from '@linux-odyssey/models'
|
||||||
import { pushToSession } from '../api/socket.js'
|
|
||||||
|
|
||||||
export interface ExecuteResult {
|
export interface ExecuteResult {
|
||||||
responses: IResponse[]
|
responses: IResponse[]
|
||||||
hints: string[]
|
hints: string[]
|
||||||
callback: () => void
|
tasks?: ITask[]
|
||||||
|
status: string
|
||||||
stage?: string
|
stage?: string
|
||||||
end?: boolean
|
end?: boolean
|
||||||
}
|
}
|
||||||
@@ -79,15 +79,17 @@ export default class SessionHandler {
|
|||||||
|
|
||||||
this.addNewTasks()
|
this.addNewTasks()
|
||||||
|
|
||||||
|
this.session.responses.push(stage.responses)
|
||||||
|
|
||||||
|
if (stage.id === 'END') {
|
||||||
|
this.session.status = 'finished'
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
responses: stage.responses,
|
responses: stage.responses,
|
||||||
hints: stage.hints,
|
hints: stage.hints,
|
||||||
callback: () => {
|
tasks: this.session.tasks,
|
||||||
pushToSession(this.session.id, 'tasks', this.session.tasks)
|
status: this.session.status,
|
||||||
if (stage.hints && stage.hints.length > 0)
|
|
||||||
pushToSession(this.session.id, 'hints', stage.hints)
|
|
||||||
pushToSession(this.session.id, 'status', this.session.status)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,11 +100,7 @@ export default class SessionHandler {
|
|||||||
return {
|
return {
|
||||||
responses,
|
responses,
|
||||||
hints,
|
hints,
|
||||||
callback: () => {
|
status: this.session.status,
|
||||||
if (hints && hints.length > 0)
|
|
||||||
pushToSession(this.session.id, 'hints', hints)
|
|
||||||
pushToSession(this.session.id, 'status', this.session.status)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,7 @@ describe('example helloworld app', () => {
|
|||||||
describe('Game Start Page UI', () => {
|
describe('Game Start Page UI', () => {
|
||||||
const title = '終端機的基本知識'
|
const title = '終端機的基本知識'
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.PrepareForGame()
|
cy.PrepareForGame('get-started')
|
||||||
})
|
})
|
||||||
it('Check Header', () => {
|
it('Check Header', () => {
|
||||||
cy.get('#GameTitle').should('contain', 'Linux Odyssey').and('be.visible')
|
cy.get('#GameTitle').should('contain', 'Linux Odyssey').and('be.visible')
|
||||||
@@ -124,7 +124,7 @@ describe('example helloworld app', () => {
|
|||||||
})
|
})
|
||||||
describe('Game Play', () => {
|
describe('Game Play', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.PrepareForGame()
|
cy.PrepareForGame('get-started')
|
||||||
})
|
})
|
||||||
it('Typing in Terminal', () => {
|
it('Typing in Terminal', () => {
|
||||||
cy.typeInCommand('12345{enter}')
|
cy.typeInCommand('12345{enter}')
|
||||||
@@ -136,7 +136,7 @@ describe('example helloworld app', () => {
|
|||||||
cy.checkTaskInit()
|
cy.checkTaskInit()
|
||||||
cy.InitTerminal()
|
cy.InitTerminal()
|
||||||
cy.typeInCommand('echo start{enter}')
|
cy.typeInCommand('echo start{enter}')
|
||||||
cy.checkPending()
|
// cy.checkPending()
|
||||||
cy.waitUntilActive()
|
cy.waitUntilActive()
|
||||||
cy.get('#Lbutton').should('be.visible').and('be.disabled')
|
cy.get('#Lbutton').should('be.visible').and('be.disabled')
|
||||||
cy.get('#Rbutton').should('be.visible').and('be.disabled')
|
cy.get('#Rbutton').should('be.visible').and('be.disabled')
|
||||||
@@ -144,7 +144,7 @@ describe('example helloworld app', () => {
|
|||||||
cy.getQuestInfo('✓ 輸入 echo start 來開始教程').should('be.visible')
|
cy.getQuestInfo('✓ 輸入 echo start 來開始教程').should('be.visible')
|
||||||
cy.getQuestInfo('➤ 在終端機輸入 echo hello').should('be.visible')
|
cy.getQuestInfo('➤ 在終端機輸入 echo hello').should('be.visible')
|
||||||
cy.typeInCommand('echo hello{enter}')
|
cy.typeInCommand('echo hello{enter}')
|
||||||
cy.checkPending()
|
// cy.checkPending()
|
||||||
cy.waitUntilActive()
|
cy.waitUntilActive()
|
||||||
cy.get('#Lbutton').should('be.visible').and('be.enabled')
|
cy.get('#Lbutton').should('be.visible').and('be.enabled')
|
||||||
cy.get('#Rbutton').should('be.visible').and('be.disabled')
|
cy.get('#Rbutton').should('be.visible').and('be.disabled')
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
describe('Complete quests with only commands', () => {
|
describe('Complete quests with only commands', () => {
|
||||||
beforeEach(() => {
|
|
||||||
cy.PrepareForGame()
|
|
||||||
})
|
|
||||||
it('Complete quest : get-started', () => {
|
it('Complete quest : get-started', () => {
|
||||||
cy.CompleteStageWithCommands('get-started')
|
cy.CompleteStageWithCommands('get-started')
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ Cypress.Commands.add('LoginWithPassword', (username, password) => {
|
|||||||
cy.get('#password').type(password)
|
cy.get('#password').type(password)
|
||||||
cy.get('#LogInOrSignUp').click()
|
cy.get('#LogInOrSignUp').click()
|
||||||
})
|
})
|
||||||
Cypress.Commands.add('PrepareForGame', () => {
|
Cypress.Commands.add('PrepareForGame', (questId) => {
|
||||||
cy.visit('/')
|
cy.visit('/')
|
||||||
// make sure the login page is loaded
|
// make sure the login page is loaded
|
||||||
cy.url().should('satisfy', (elements: string) => {
|
cy.url().should('satisfy', (elements: string) => {
|
||||||
@@ -51,8 +51,9 @@ Cypress.Commands.add('PrepareForGame', () => {
|
|||||||
)
|
)
|
||||||
// make sure the map page is loaded
|
// make sure the map page is loaded
|
||||||
cy.url().should('include', '/map')
|
cy.url().should('include', '/map')
|
||||||
cy.visit('/game/get-started')
|
const url = `/game/${questId}`
|
||||||
cy.url().should('include', '/game/get-started')
|
cy.visit(url)
|
||||||
|
cy.url().should('include', url)
|
||||||
// check task first so the terminal has better possibility loaded
|
// check task first so the terminal has better possibility loaded
|
||||||
cy.checkTaskInit()
|
cy.checkTaskInit()
|
||||||
cy.get('.xterm-screen', { timeout: 10000 })
|
cy.get('.xterm-screen', { timeout: 10000 })
|
||||||
@@ -114,8 +115,7 @@ Cypress.Commands.add('checkPending', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
Cypress.Commands.add('CompleteStageWithCommands', (stagename) => {
|
Cypress.Commands.add('CompleteStageWithCommands', (stagename) => {
|
||||||
cy.visit(`/game/${stagename}`)
|
cy.PrepareForGame(stagename)
|
||||||
cy.url().should('include', `/game/${stagename}`, { timeout: 50000 })
|
|
||||||
cy.log(`Completing stage :${stagename}`)
|
cy.log(`Completing stage :${stagename}`)
|
||||||
cy.checkTaskInit()
|
cy.checkTaskInit()
|
||||||
cy.CheckTextElement('#reset', '重來', 'Reset').click()
|
cy.CheckTextElement('#reset', '重來', 'Reset').click()
|
||||||
@@ -123,19 +123,22 @@ Cypress.Commands.add('CompleteStageWithCommands', (stagename) => {
|
|||||||
cy.InitTerminal()
|
cy.InitTerminal()
|
||||||
cy.readFile(`../quests/${stagename}/answer.sh`, 'utf-8').then((answers) => {
|
cy.readFile(`../quests/${stagename}/answer.sh`, 'utf-8').then((answers) => {
|
||||||
const answerarr = answers.split('\n')
|
const answerarr = answers.split('\n')
|
||||||
for (const element of answerarr) {
|
answerarr.forEach((element, index) => {
|
||||||
cy.typeInCommand(`${element}{enter}`)
|
cy.typeInCommand(`${element}{enter}`)
|
||||||
if (answerarr.indexOf(element) + 1 !== answerarr.length) {
|
if (index < answerarr.length - 1) {
|
||||||
cy.checkPending()
|
|
||||||
cy.waitUntilActive()
|
cy.waitUntilActive()
|
||||||
|
} else {
|
||||||
|
cy.waitUntilActive(true).then(() => {
|
||||||
|
cy.CheckTextElement(
|
||||||
|
'#QuestCompleted',
|
||||||
|
'關卡完成!',
|
||||||
|
'Quest Completed!'
|
||||||
|
)
|
||||||
|
cy.get('div[class="modal"]').find('p').should('be.visible')
|
||||||
|
cy.get('#BacktoMap').should('be.visible').and('contain', '回到地圖')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if (answerarr.indexOf(element) + 1 === answerarr.length) {
|
})
|
||||||
cy.waitUntilActive(true)
|
|
||||||
cy.CheckTextElement('#QuestCompleted', '關卡完成!', 'Quest Completed!')
|
|
||||||
cy.get('div[class="modal"]').find('p').should('be.visible')
|
|
||||||
cy.get('#BacktoMap').should('be.visible').and('contain', '回到地圖')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
Cypress.Commands.add('CheckTextElement', (id, chText, enText) => {
|
Cypress.Commands.add('CheckTextElement', (id, chText, enText) => {
|
||||||
|
|||||||
Reference in new issue
Block a user