test(studio): Modify.vue 单测(4) + 修单测逮到的 i18n 占位符 bug
- src/views/create/Modify.spec.ts:验三方式提交各构造契约合法请求——扩展→extend、改玩法→modify(regenerate-module,空目标默认首个 behavior)、调参→modify(deterministic,JSON 解析)、JSON 非法→Toast 不提交;记 vant mock 须 vi.hoisted(直接返回非嵌套→被提升)坑
- 修 i18n bug(单测逮到):modify.tweakValuePlaceholder `如 {"speed":5}` 的 `{}` 被 vue-i18n 当插值语法("Invalid token in placeholder")→占位符 mis-render;改为无花括号 `JSON,如 "speed": 5`(zh/en)
验证:`npm test` 16/16 绿(i18n 编译告警消除);`npm run build` 绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
644b95a3c9
commit
7980c717be
@ -70,7 +70,7 @@ export default {
|
||||
tweakPathLabel: 'Path',
|
||||
tweakPathPlaceholder: 'e.g. /config/speed',
|
||||
tweakValueLabel: 'New value (JSON)',
|
||||
tweakValuePlaceholder: 'e.g. {"speed": 5}',
|
||||
tweakValuePlaceholder: 'JSON, e.g. "speed": 5',
|
||||
valueInvalid: 'New value is not valid JSON',
|
||||
emptyIntent: 'Please enter a description first',
|
||||
emptyPath: 'Please enter the path to modify',
|
||||
|
||||
@ -82,7 +82,7 @@ export default {
|
||||
tweakPathLabel: '路径',
|
||||
tweakPathPlaceholder: '如 /config/speed',
|
||||
tweakValueLabel: '新值(JSON)',
|
||||
tweakValuePlaceholder: '如 {"speed": 5}',
|
||||
tweakValuePlaceholder: 'JSON,如 "speed": 5',
|
||||
valueInvalid: '新值不是合法 JSON,请检查',
|
||||
emptyIntent: '请先填写描述',
|
||||
emptyPath: '请填写要修改的路径',
|
||||
|
||||
100
game-studio/src/views/create/Modify.spec.ts
Normal file
100
game-studio/src/views/create/Modify.spec.ts
Normal file
@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Modify.spec.ts —— 改进入口组件单测(plan 002 U6)。
|
||||
* 验三方式提交各构造【契约合法】请求:扩展→extend / 改玩法→modify(regenerate-module) / 调参→modify(deterministic);
|
||||
* 调参 JSON 非法 → Toast 不提交。经 mock store/router/vant 隔离。跑 `npm test`。
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { mount, flushPromises } from '@vue/test-utils'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
// router push 间谍
|
||||
const push = vi.fn()
|
||||
const back = vi.fn()
|
||||
vi.mock('vue-router', () => ({ useRouter: () => ({ push, back }) }))
|
||||
|
||||
// vant showToast 间谍(JSON 非法路径校验)。
|
||||
// 注:vant mock 工厂【直接返回】showToast(非嵌套在函数内),会被 vi.mock 提升到顶→须用 vi.hoisted 先建间谍,
|
||||
// 否则「Cannot access 'showToast' before initialization」(router/store 的 spy 嵌在函数内惰性引用故无此问题)。
|
||||
const { showToast } = vi.hoisted(() => ({ showToast: vi.fn() }))
|
||||
vi.mock('vant', () => ({ showToast }))
|
||||
|
||||
// store 间谍(直接断言 Modify 构造的请求对象)
|
||||
const modify = vi.fn().mockResolvedValue({ id: 1002, gameId: 1 })
|
||||
const extend = vi.fn().mockResolvedValue({ id: 1003, gameId: 1 })
|
||||
vi.mock('../../store/create', () => ({ useCreateStore: () => ({ modify, extend }) }))
|
||||
|
||||
import Modify from './Modify.vue'
|
||||
|
||||
const i18n = createI18n({ legacy: false, locale: 'zh-CN', messages: { 'zh-CN': {} }, missingWarn: false, fallbackWarn: false })
|
||||
const AppButtonStub = {
|
||||
props: ['disabled', 'loading', 'block', 'size', 'type'],
|
||||
emits: ['click'],
|
||||
template: '<button class="app-btn" :disabled="disabled" @click="$emit(\'click\')"><slot/></button>',
|
||||
}
|
||||
|
||||
function mountModify() {
|
||||
return mount(Modify, {
|
||||
props: { versionId: '9001' },
|
||||
global: { plugins: [i18n], stubs: { Icon: true, AppButton: AppButtonStub } },
|
||||
})
|
||||
}
|
||||
|
||||
describe('Modify.vue(改进入口·三方式→契约请求)', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
modify.mockResolvedValue({ id: 1002, gameId: 1 })
|
||||
extend.mockResolvedValue({ id: 1003, gameId: 1 })
|
||||
})
|
||||
|
||||
it('扩展(默认 tab) → extend({baseVersionId, intent}) + 跳进度页', async () => {
|
||||
const w = mountModify()
|
||||
await w.find('.modify-body textarea').setValue('再加一个 boss 关卡')
|
||||
await w.find('.modify-foot .app-btn').trigger('click')
|
||||
await flushPromises()
|
||||
expect(extend).toHaveBeenCalledWith({ baseVersionId: 9001, intent: '再加一个 boss 关卡' })
|
||||
expect(modify).not.toHaveBeenCalled()
|
||||
expect(push).toHaveBeenCalledOnce()
|
||||
expect(push.mock.calls[0][0].params.taskId).toBe('1003')
|
||||
})
|
||||
|
||||
it('改玩法 tab → modify(regenerate-module, kind=behavior, payload.intent);目标空走默认首个 behavior 路径', async () => {
|
||||
const w = mountModify()
|
||||
await w.findAll('.modify-body .seg-item')[1].trigger('click') // 改玩法
|
||||
await w.find('.modify-body textarea').setValue('把陨石改成会左右摇摆')
|
||||
await w.find('.modify-foot .app-btn').trigger('click')
|
||||
await flushPromises()
|
||||
expect(modify).toHaveBeenCalledOnce()
|
||||
const arg = modify.mock.calls[0][0]
|
||||
expect(arg.mode).toBe('regenerate-module')
|
||||
expect(arg.target.kind).toBe('behavior')
|
||||
expect(arg.target.path).toBe('/gameDefinition/behaviors/0') // 空目标 → 默认首个
|
||||
expect(arg.payload.intent).toBe('把陨石改成会左右摇摆')
|
||||
})
|
||||
|
||||
it('调参 tab → modify(deterministic, kind/path, payload.value 解析 JSON)', async () => {
|
||||
const w = mountModify()
|
||||
await w.findAll('.modify-body .seg-item')[2].trigger('click') // 调参(默认 kind=config)
|
||||
const inputs = w.findAll('.modify-body input')
|
||||
await inputs[0].setValue('/config/speed') // path
|
||||
await w.find('.modify-body textarea').setValue('{"speed": 5}')
|
||||
await w.find('.modify-foot .app-btn').trigger('click')
|
||||
await flushPromises()
|
||||
expect(modify).toHaveBeenCalledOnce()
|
||||
const arg = modify.mock.calls[0][0]
|
||||
expect(arg.mode).toBe('deterministic')
|
||||
expect(arg.target).toEqual({ kind: 'config', path: '/config/speed' })
|
||||
expect(arg.payload.value).toEqual({ speed: 5 })
|
||||
})
|
||||
|
||||
it('调参 JSON 非法 → showToast 且不提交', async () => {
|
||||
const w = mountModify()
|
||||
await w.findAll('.modify-body .seg-item')[2].trigger('click')
|
||||
await w.findAll('.modify-body input')[0].setValue('/config/speed')
|
||||
await w.find('.modify-body textarea').setValue('{速度: 5}') // 非法 JSON
|
||||
await w.find('.modify-foot .app-btn').trigger('click')
|
||||
await flushPromises()
|
||||
expect(showToast).toHaveBeenCalledOnce()
|
||||
expect(modify).not.toHaveBeenCalled()
|
||||
expect(push).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user