mirror of
https://github.com/linux-odyssey/linux-odyssey.git
synced 2026-11-05 19:20:12 +08:00
feat: add quest class and session getActiveStages
This commit is contained in:
1 parent
471b781d85
commit
e622011b21
13 files changed
+127
-49
No files matched your search
+1
-2
@@ -49,7 +49,7 @@ module.exports = {
|
||||
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
'warn',
|
||||
{
|
||||
args: 'after-used',
|
||||
argsIgnorePattern: '^_',
|
||||
@@ -73,7 +73,6 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
'no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'error',
|
||||
'no-useless-constructor': 'off',
|
||||
'@typescript-eslint/no-useless-constructor': 'error',
|
||||
'no-empty-function': 'off',
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { IQuest, IStage } from './schema'
|
||||
import { Stage } from './Stage'
|
||||
|
||||
export class Quest {
|
||||
private stages: Stage[]
|
||||
private quest: IQuest
|
||||
|
||||
constructor(quest: IQuest) {
|
||||
this.quest = quest
|
||||
this.stages = quest.stages.map((stage: IStage) => new Stage(stage))
|
||||
}
|
||||
|
||||
getActiveStages(completed: string[]) {
|
||||
return this.stages.filter((stage) => stage.active(completed))
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
import { Quest } from './Quest'
|
||||
import { IQuest } from './schema'
|
||||
import { ISession } from './types'
|
||||
|
||||
export class Session implements ISession {
|
||||
private completed: string[] = []
|
||||
private quest: Quest
|
||||
|
||||
constructor({ completedStages }: ISession) {
|
||||
constructor({ completedStages }: ISession, quest: IQuest) {
|
||||
this.completed = completedStages
|
||||
this.quest = new Quest(quest)
|
||||
}
|
||||
|
||||
get completedStages() {
|
||||
@@ -14,4 +18,8 @@ export class Session implements ISession {
|
||||
complete(stageId: string) {
|
||||
this.completed.push(stageId)
|
||||
}
|
||||
|
||||
getActiveStages(): string[] {
|
||||
return this.quest.getActiveStages(this.completed).map((stage) => stage.id)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IStage, ICommand, IFileExistenceChecker, ISession } from './types'
|
||||
import { IStage, ICommand, IFileExistenceChecker } from './types'
|
||||
import { Condition } from './condition/Condition'
|
||||
|
||||
export class Stage {
|
||||
@@ -21,17 +21,15 @@ export class Stage {
|
||||
return this.condition.match(command) && this.condition.check(checker)
|
||||
}
|
||||
|
||||
private checkRequirements(session: ISession): boolean {
|
||||
return this.requirements.every((req) =>
|
||||
session.completedStages.includes(req)
|
||||
)
|
||||
private checkRequirements(completed: string[]): boolean {
|
||||
return this.requirements.every((req) => completed.includes(req))
|
||||
}
|
||||
|
||||
private finished(session: ISession): boolean {
|
||||
return session.completedStages.includes(this.id)
|
||||
private finished(completed: string[]): boolean {
|
||||
return completed.includes(this.id)
|
||||
}
|
||||
|
||||
active(session: ISession): boolean {
|
||||
return !this.finished(session) && this.checkRequirements(session)
|
||||
active(completed: string[]): boolean {
|
||||
return !this.finished(completed) && this.checkRequirements(completed)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
ICommand,
|
||||
ICondition,
|
||||
IFileExistenceChecker,
|
||||
FileExistenceInput,
|
||||
} from '../types.js'
|
||||
import { ICondition } from '../schema/condition.js'
|
||||
import { PwdMatcher } from './PwdMatcher.js'
|
||||
import { CommandMatcher } from './CommandMatcher.js'
|
||||
import { ErrorMatcher } from './ErrorMatcher.js'
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { z } from 'zod'
|
||||
import { FileType } from '../types.js'
|
||||
|
||||
export const fileInputSchema = z.object({
|
||||
path: z.string(),
|
||||
type: z.enum(['file', 'folder']),
|
||||
type: z.nativeEnum(FileType),
|
||||
})
|
||||
|
||||
export type IFileInput = z.infer<typeof fileInputSchema>
|
||||
@@ -28,6 +29,6 @@ export type ICondition = z.infer<typeof baseConditionSchema> & {
|
||||
|
||||
export const conditionSchema: z.ZodType<ICondition> =
|
||||
baseConditionSchema.extend({
|
||||
or: z.lazy(() => z.array(conditionSchema)).optional(),
|
||||
not: z.lazy(() => conditionSchema).optional(),
|
||||
or: z.lazy(() => z.array(conditionSchema).optional()),
|
||||
not: z.lazy(() => conditionSchema.optional()),
|
||||
})
|
||||
@@ -1,7 +1,5 @@
|
||||
export { conditionSchema, ICondition } from './condition.js'
|
||||
export { stageSchema, IStage } from './stage.js'
|
||||
export {
|
||||
requirementsSchema,
|
||||
IRequirements,
|
||||
responseSchema,
|
||||
} from './requirements.js'
|
||||
export { requirementsSchema, IRequirements } from './requirements.js'
|
||||
export { responseSchema, IResponse } from './response.js'
|
||||
export { questSchema, IQuest } from './quest.js'
|
||||
@@ -0,0 +1,17 @@
|
||||
import { z } from 'zod'
|
||||
import { stageSchema } from './stage.js'
|
||||
import { requirementsSchema } from './requirements.js'
|
||||
// import { globalExceptionSchema } from './exception.js'
|
||||
// import { requirementSchema } from './requirement.js'
|
||||
|
||||
export const questSchema = z.object({
|
||||
id: z.string(),
|
||||
title: z.string(),
|
||||
image: z.string(),
|
||||
instruction: z.string(),
|
||||
requirements: requirementsSchema,
|
||||
stages: z.array(stageSchema),
|
||||
// exceptions: z.array(globalExceptionSchema).default([]),
|
||||
})
|
||||
|
||||
export type IQuest = z.infer<typeof questSchema>
|
||||
@@ -1,12 +1,5 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
export const requirementsSchema = z.object({
|
||||
requirements: z.array(z.string()),
|
||||
})
|
||||
export const requirementsSchema = z.array(z.string())
|
||||
|
||||
export type IRequirements = z.infer<typeof requirementsSchema>
|
||||
|
||||
export const responseSchema = z.object({
|
||||
type: z.enum(['narrative', 'dialogue']),
|
||||
content: z.string(),
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
export const responseSchema = z.object({
|
||||
type: z.enum(['narrative', 'dialogue']),
|
||||
content: z.string(),
|
||||
})
|
||||
|
||||
export type IResponse = z.infer<typeof responseSchema>
|
||||
@@ -1,5 +1,6 @@
|
||||
import { z } from 'zod'
|
||||
import { conditionSchema, responseSchema } from './index.js'
|
||||
import { conditionSchema } from './condition.js'
|
||||
import { responseSchema } from './response.js'
|
||||
|
||||
export const stageSchema = z.object({
|
||||
id: z.string().regex(/^[a-zA-Z0-9_-]+$/),
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { expect, describe, it } from '@jest/globals'
|
||||
import { IQuest } from '../src/schema'
|
||||
import { Session } from '../src/Session'
|
||||
|
||||
const quest: IQuest = {
|
||||
id: 'quest1',
|
||||
title: 'Quest 1',
|
||||
image: 'image1',
|
||||
instruction: 'Instruction 1',
|
||||
requirements: [],
|
||||
stages: [
|
||||
{
|
||||
id: 'stage1',
|
||||
requirements: [],
|
||||
condition: {},
|
||||
response: { type: 'narrative', content: 'Narrative 1' },
|
||||
},
|
||||
{
|
||||
id: 'stage2',
|
||||
requirements: ['stage1'],
|
||||
condition: {},
|
||||
response: { type: 'narrative', content: 'Narrative 2' },
|
||||
},
|
||||
{
|
||||
id: 'stage3',
|
||||
requirements: ['stage1'],
|
||||
condition: {},
|
||||
response: { type: 'narrative', content: 'Narrative 3' },
|
||||
},
|
||||
{
|
||||
id: 'stage4',
|
||||
requirements: ['stage2', 'stage3'],
|
||||
condition: {},
|
||||
response: { type: 'narrative', content: 'Narrative 4' },
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
describe('Session', () => {
|
||||
it('should return active stages', () => {
|
||||
const session = new Session(
|
||||
{
|
||||
completedStages: [],
|
||||
},
|
||||
quest
|
||||
)
|
||||
expect(session.getActiveStages()).toEqual(['stage1'])
|
||||
|
||||
session.complete('stage1')
|
||||
expect(session.getActiveStages()).toEqual(['stage2', 'stage3'])
|
||||
|
||||
session.complete('stage2')
|
||||
expect(session.getActiveStages()).toEqual(['stage3'])
|
||||
|
||||
session.complete('stage3')
|
||||
expect(session.getActiveStages()).toEqual(['stage4'])
|
||||
})
|
||||
})
|
||||
@@ -2,7 +2,6 @@ import { test, expect } from '@jest/globals'
|
||||
import { Stage } from '../src/Stage'
|
||||
import { FileType } from '../src/types'
|
||||
import { MockFileChecker } from './asyncCondition.test'
|
||||
import { Session } from '../src/Session'
|
||||
|
||||
const checker = new MockFileChecker()
|
||||
|
||||
@@ -68,21 +67,3 @@ test('check stage with async condition falsy', async () => {
|
||||
})
|
||||
expect(await stage.satisfies({}, checker)).toBe(false)
|
||||
})
|
||||
|
||||
test('stage active checks', () => {
|
||||
const session = new Session({
|
||||
completedStages: ['stage1'],
|
||||
})
|
||||
const stage3 = new Stage({
|
||||
id: 'stage3',
|
||||
requirements: ['stage1', 'stage2'],
|
||||
condition: {},
|
||||
})
|
||||
expect(stage3.active(session)).toBe(false)
|
||||
|
||||
session.complete('stage2')
|
||||
expect(stage3.active(session)).toBe(true)
|
||||
|
||||
session.complete('stage3')
|
||||
expect(stage3.active(session)).toBe(false)
|
||||
})
|
||||
Reference in new issue
Block a user