openxiangda-cli 2.0.0-alpha.182 → 2.0.0-alpha.185

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/base.js +2 -2
  2. package/dist/base.js.map +1 -1
  3. package/dist/commands/admin.d.ts.map +1 -1
  4. package/dist/commands/admin.js +5 -3
  5. package/dist/commands/admin.js.map +1 -1
  6. package/dist/commands/create.d.ts +1 -0
  7. package/dist/commands/create.d.ts.map +1 -1
  8. package/dist/commands/create.js +34 -16
  9. package/dist/commands/create.js.map +1 -1
  10. package/dist/commands/dev.d.ts.map +1 -1
  11. package/dist/commands/dev.js +1 -1
  12. package/dist/commands/dev.js.map +1 -1
  13. package/dist/commands/login.js +1 -1
  14. package/dist/commands/login.js.map +1 -1
  15. package/dist/commands/spec.js +1 -1
  16. package/dist/commands/spec.js.map +1 -1
  17. package/package.json +5 -5
  18. package/template/AGENTS.md +2 -2
  19. package/template/apps/web/e2e/README.md +7 -0
  20. package/template/apps/web/package.json +2 -2
  21. package/template/apps/web/playwright.config.ts +3 -5
  22. package/template/apps/web/vite.config.ts +1 -9
  23. package/template/appspec/app.md +5 -1
  24. package/template/package.json +1 -1
  25. package/template/apps/web/data-api-live.e2e.html +0 -15
  26. package/template/apps/web/e2e/client-fixture.ts +0 -5
  27. package/template/apps/web/e2e/component-defaults-fixture.css +0 -15
  28. package/template/apps/web/e2e/data-api-live-fixture.tsx +0 -162
  29. package/template/apps/web/e2e/data-api-live.spec.ts +0 -144
  30. package/template/apps/web/e2e/field-protocol-fixture.tsx +0 -387
  31. package/template/apps/web/e2e/field-protocol.spec.ts +0 -285
  32. package/template/apps/web/e2e/mobile-files.spec.ts +0 -96
  33. package/template/apps/web/e2e/mobile-reference-fixture.tsx +0 -314
  34. package/template/apps/web/e2e/mobile-reference.spec.ts +0 -360
  35. package/template/apps/web/e2e/mobile-selection.spec.ts +0 -199
  36. package/template/apps/web/e2e/resource-experience-fixture.tsx +0 -491
  37. package/template/apps/web/e2e/resource-platform-mock.ts +0 -202
  38. package/template/apps/web/e2e/resources.spec.ts +0 -488
  39. package/template/apps/web/e2e/standard-admin.spec.ts +0 -511
  40. package/template/apps/web/e2e/workflow-entry-fixture.tsx +0 -107
  41. package/template/apps/web/e2e/workflow-entry.spec.ts +0 -556
  42. package/template/apps/web/e2e/workflow-experience-fixture.tsx +0 -338
  43. package/template/apps/web/e2e/workflow.spec.ts +0 -1265
  44. package/template/apps/web/field-protocol.e2e.html +0 -22
  45. package/template/apps/web/mobile-reference.e2e.html +0 -27
  46. package/template/apps/web/resource-experience.e2e.html +0 -16
  47. package/template/apps/web/scripts/check.mjs +0 -43
  48. package/template/apps/web/test/contracts.test.ts +0 -163
  49. package/template/apps/web/workflow-entry.e2e.html +0 -15
  50. package/template/apps/web/workflow-experience.e2e.html +0 -15
@@ -1,285 +0,0 @@
1
- import { expect, test } from '@playwright/test';
2
-
3
- const fieldCount = 38;
4
-
5
- test.describe('Field Kit protocol acceptance', () => {
6
- test.use({
7
- geolocation: { longitude: 120.123456, latitude: 30.234567 },
8
- permissions: ['geolocation'],
9
- });
10
-
11
- test.beforeEach(async ({ page }) => {
12
- await page.route('**/china-divisions/**', async route => {
13
- await route.fulfill({
14
- contentType: 'application/json',
15
- json: {
16
- code: 200,
17
- data: [{
18
- adcode: '330000',
19
- name: '浙江省',
20
- level: 'province',
21
- hasChildren: true,
22
- }],
23
- },
24
- });
25
- });
26
- await page.route('**/files/*/content?*', async route => {
27
- await route.fulfill({
28
- body: Buffer.from(
29
- 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=',
30
- 'base64'
31
- ),
32
- contentType: 'image/png',
33
- status: 200,
34
- });
35
- });
36
- });
37
-
38
- test('renders and operates every desktop edit/read component', async ({ page }) => {
39
- const pageErrors: string[] = [];
40
- const addressRequests: string[] = [];
41
- page.on('pageerror', error => pageErrors.push(error.message));
42
- page.on('request', request => {
43
- if (request.url().includes('/china-divisions/')) {
44
- addressRequests.push(request.url());
45
- }
46
- });
47
- await page.setViewportSize({ width: 1440, height: 1100 });
48
- await page.goto('/field-protocol.e2e.html');
49
- await expect(page.locator('[data-field-code]')).toHaveCount(fieldCount);
50
- await expect(page.locator('[data-read-field]')).toHaveCount(fieldCount);
51
- await expect(page.locator('[data-audit-field]')).toHaveCount(fieldCount);
52
- await expect(page.locator('[data-field-code="静态单选下拉"] .ant-select')).toBeVisible();
53
- await expect(page.locator('[data-field-code="单选按钮"] .ant-radio-group')).toBeVisible();
54
- await expect(page.locator('[data-field-code="复选框"] .ant-checkbox-group')).toBeVisible();
55
- await expect(page.locator('[data-field-code="时间"] input')).toBeVisible();
56
- await expect(page.locator('[data-field-code="精确定位"] input')).toHaveCount(0);
57
- await expect(page.locator('[data-field-code="精确定位"]')).not.toContainText('手工');
58
- await expect(page.locator('.ant-alert-error')).toHaveCount(0);
59
- await expect.poll(() => addressRequests.length).toBeGreaterThan(0);
60
- expect(addressRequests.every(url => new URL(url).pathname.startsWith('/service/china-divisions/'))).toBe(true);
61
-
62
- await page.getByRole('button', { name: '验证表单' }).click();
63
- await expect(page.locator('[data-field-code="UUID"] .ant-form-item-has-error')).toHaveCount(0);
64
-
65
- await expect(page.locator('[data-read-field="图片"] img.oxa-file-thumbnail')).toHaveAttribute('src', /blob:/);
66
- await expect(page.locator('[data-read-field="富文本"] img')).toHaveAttribute('src', /blob:/);
67
- expect(await page.locator('[data-read-field="图片"] img.oxa-file-thumbnail').evaluate(
68
- image => (image as HTMLImageElement).naturalWidth
69
- )).toBeGreaterThan(0);
70
- expect(await page.locator('[data-read-field="富文本"] img').evaluate(
71
- image => (image as HTMLImageElement).naturalWidth
72
- )).toBeGreaterThan(0);
73
- await expect(page.locator('[aria-label="当前协议字段变更记录"]')).not.toContainText('[object Object]');
74
- await expect(page.locator('[data-audit-digest="rich-text"]')).toContainText('大值摘要');
75
-
76
- await page.locator('[data-field-code="精确定位"]').getByRole('button', { name: '浏览器定位' }).click();
77
- await expect(page.locator('[data-field-code="精确定位"]')).toContainText('120.123456');
78
- await expect(page.locator('[data-field-code="精确定位"]')).toContainText('30.234567');
79
-
80
- await page
81
- .locator('[data-field-code="业务签名"]')
82
- .getByRole('button', { name: /(?:开始|重新)签名/ })
83
- .click();
84
- const canvas = page.locator('canvas[aria-label="手写签名画布"]');
85
- await expect(canvas).toBeVisible();
86
- const box = await canvas.boundingBox();
87
- expect(box).not.toBeNull();
88
- await canvas.dispatchEvent('pointerdown', {
89
- pointerId: 1,
90
- pointerType: 'pen',
91
- buttons: 1,
92
- clientX: box!.x + 30,
93
- clientY: box!.y + 40,
94
- });
95
- for (let step = 1; step <= 8; step += 1) {
96
- await canvas.dispatchEvent('pointermove', {
97
- pointerId: 1,
98
- pointerType: 'pen',
99
- buttons: 1,
100
- clientX: box!.x + 30 + (130 * step) / 8,
101
- clientY: box!.y + 40 + (80 * step) / 8,
102
- });
103
- }
104
- await canvas.dispatchEvent('pointerup', {
105
- pointerId: 1,
106
- pointerType: 'pen',
107
- buttons: 0,
108
- clientX: box!.x + 160,
109
- clientY: box!.y + 120,
110
- });
111
- const saveSignature = page.getByRole('button', { name: '保存签名' });
112
- await expect(saveSignature).toBeEnabled();
113
- await saveSignature.click();
114
- await expect(page.locator('[data-field-code="业务签名"]')).not.toContainText('尚未签名');
115
- await expect(page.getByRole('dialog', { name: '手写签名' })).toBeHidden();
116
-
117
- expect(pageErrors).toEqual([]);
118
-
119
- expect(
120
- await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth)
121
- ).toBe(true);
122
- await page.screenshot({ fullPage: true, path: 'test-results/field-protocol-desktop.png' });
123
- });
124
-
125
- test('resource selectors query once when selection changes', async ({ page }) => {
126
- const requestCounts = new Map<string, number>();
127
- await page.route('**/source/query', async route => {
128
- const match = new URL(route.request().url()).pathname.match(/\/fields\/([^/]+)\/source\/query$/);
129
- const fieldCode = decodeURIComponent(match?.[1] || 'unknown');
130
- requestCounts.set(fieldCode, (requestCounts.get(fieldCode) || 0) + 1);
131
- const body = route.request().postDataJSON() as { operation?: 'create' | 'update' };
132
- await route.fulfill({
133
- contentType: 'application/json',
134
- json: {
135
- schemaVersion: 'openxiangda.data-field-source-page/v2',
136
- environment: {
137
- id: 'field-protocol-environment',
138
- key: 'preproduction',
139
- activeAppVersionId: 'field-protocol-version',
140
- headRevision: 1,
141
- authzRevisionId: 'field-protocol-authz',
142
- authzVersion: 1,
143
- scopeDataVersion: 'field-protocol-scope',
144
- },
145
- resourceCode: 'field-records',
146
- fieldCode,
147
- sourceResourceCode: 'field-lookups',
148
- operation: body.operation || 'create',
149
- items: [{
150
- label: `候选-${fieldCode}`,
151
- value: `candidate-${fieldCode}`,
152
- resourceCode: 'field-lookups',
153
- snapshot: { code: `code-${fieldCode}` },
154
- }],
155
- nextCursor: null,
156
- },
157
- });
158
- });
159
-
160
- await page.goto('/field-protocol.e2e.html');
161
- await expect(page.locator('[data-field-code]')).toHaveCount(fieldCount);
162
- await page.waitForLoadState('networkidle');
163
- for (const fieldCode of ['资源单选', '资源多选']) {
164
- const combobox = page
165
- .locator(`[data-field-code="${fieldCode}"]`)
166
- .getByRole('combobox');
167
- await expect(combobox).toBeVisible();
168
- await combobox.click();
169
- const dropdown = page.locator('.ant-select-dropdown:visible');
170
- await expect(dropdown).toBeVisible();
171
- await expect.poll(() => requestCounts.get(fieldCode) || 0).toBe(1);
172
- await dropdown
173
- .locator('.ant-select-item-option')
174
- .filter({ hasText: `候选-${fieldCode}` })
175
- .click();
176
- await page.waitForTimeout(500);
177
- expect(requestCounts.get(fieldCode)).toBe(1);
178
- await page.keyboard.press('Escape');
179
- await expect(dropdown).toBeHidden();
180
- }
181
- });
182
-
183
- test('mobile selection, numeric validation and save/reload keep canonical values', async ({ page }) => {
184
- const pageErrors: string[] = [];
185
- page.on('pageerror', error => pageErrors.push(error.message));
186
- await page.setViewportSize({ width: 390, height: 844 });
187
- await page.goto('/field-protocol.e2e.html?mode=mobile');
188
- const field = (label: string) => page.locator(`[data-field-code="${label}"]`);
189
- await field('单行文本').getByRole('textbox').fill('移动录入验证');
190
- await field('整数').getByRole('textbox').fill('1.5');
191
- await page.getByRole('button', { name: '验证表单', exact: true }).click();
192
- await expect(field('整数').getByRole('alert')).toContainText('整数');
193
- await expect(page.locator('[data-saved-values]')).toBeEmpty();
194
- await field('整数').getByRole('textbox').fill('12');
195
- await field('小数').getByRole('textbox').fill('12.75');
196
- await field('金额').getByRole('textbox').fill('');
197
- const moneyInput = await field('金额').getByRole('textbox').boundingBox();
198
- const currency = await field('金额').locator('[aria-hidden="true"]').first().boundingBox();
199
- expect(moneyInput!.height).toBeGreaterThanOrEqual(24);
200
- // Currency must stay beside the editable value, even with the full React stylesheet.
201
- expect(currency!.y + currency!.height / 2).toBeGreaterThan(moneyInput!.y);
202
- expect(currency!.y + currency!.height / 2).toBeLessThan(moneyInput!.y + moneyInput!.height);
203
-
204
- const choice = field('静态单选下拉');
205
- const oldChoice = await choice.getByRole('button', { name: '选择静态单选下拉' }).innerText();
206
- await choice.getByRole('button', { name: '选择静态单选下拉' }).click();
207
- await choice.getByText('停用', { exact: true }).last().click();
208
- await choice.getByRole('button', { name: '取消', exact: true }).click();
209
- await expect(choice.getByRole('button', { name: '选择静态单选下拉' })).toHaveText(oldChoice);
210
- await choice.getByRole('button', { name: '选择静态单选下拉' }).click();
211
- await choice.getByText('停用', { exact: true }).last().click();
212
- await choice.getByRole('button', { name: '确定', exact: true }).click();
213
- await expect(choice.getByRole('button', { name: '选择静态单选下拉' })).toHaveText('停用');
214
-
215
- await field('日期').getByRole('button', { name: '选择日期', exact: true }).click();
216
- await field('日期').getByText('确定', { exact: true }).click();
217
- await field('时间').getByRole('button', { name: '选择时间', exact: true }).click();
218
- await field('时间').getByText('确定', { exact: true }).click();
219
- await page.getByRole('button', { name: '验证表单', exact: true }).click();
220
- const result = page.locator('[data-saved-values]');
221
- await expect(result).toContainText('移动录入验证');
222
- const stored = JSON.parse(await result.innerText());
223
- expect(stored['整数']).toBe(12);
224
- expect(stored['小数']).toBe(12.75);
225
- expect(stored['金额']).toBeNull();
226
- expect(stored['静态单选下拉']).toEqual({ label: '停用', value: 'disabled' });
227
- expect(stored['日期']).toMatch(/^\d{4}-\d{2}-\d{2}$/);
228
- expect(stored['时间']).toMatch(/^\d{2}:\d{2}:\d{2}$/);
229
- await field('单行文本').getByRole('textbox').fill('未保存修改');
230
- await page.getByRole('button', { name: '重新载入已保存值' }).click();
231
- await expect(field('单行文本').getByRole('textbox')).toHaveValue('移动录入验证');
232
- await expect(field('小数').getByRole('textbox')).toHaveValue('12.75');
233
- await field('日期').getByRole('button', { name: '清空日期', exact: true }).click();
234
- await page.getByRole('button', { name: '验证表单', exact: true }).click();
235
- await expect.poll(async () => JSON.parse(await result.innerText())['日期']).toBeNull();
236
- expect(pageErrors).toEqual([]);
237
- await page.evaluate(() => window.scrollTo(0, 0));
238
- await page.screenshot({ path: 'test-results/foundation-mobile-controls.png' });
239
- });
240
-
241
- test('renders every mobile control without horizontal overflow', async ({ page }) => {
242
- await page.setViewportSize({ width: 390, height: 844 });
243
- await page.goto('/field-protocol.e2e.html?mode=mobile');
244
- await expect(page.locator('[data-field-code]')).toHaveCount(fieldCount);
245
- await expect(page.locator('.oxa-mobile-field')).toHaveCount(fieldCount);
246
- await expect(page.locator('[data-field-code="精确定位"] input')).toHaveCount(0);
247
- await expect(page.locator('[data-field-code="附件"] input[type="file"]')).toBeHidden();
248
- await expect(page.locator('.ant-alert-error')).toHaveCount(0);
249
- expect(
250
- await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth)
251
- ).toBe(true);
252
- await page.screenshot({ fullPage: true, path: 'test-results/field-protocol-mobile.png' });
253
- });
254
-
255
- test('mobile default components and popups do not reset the surrounding page', async ({ page }) => {
256
- const pageErrors: string[] = [];
257
- page.on('pageerror', error => pageErrors.push(error.message));
258
- await page.setViewportSize({ width: 390, height: 844 });
259
- await page.goto('/field-protocol.e2e.html?mode=mobile-surface');
260
- const outsideStyles = () => page.evaluate(() => ({
261
- background: getComputedStyle(document.documentElement).backgroundColor,
262
- color: getComputedStyle(document.body).color,
263
- fontSize: getComputedStyle(document.body).fontSize,
264
- fontFamily: getComputedStyle(document.body).fontFamily,
265
- link: getComputedStyle(document.getElementById('outside-mobile')!).color,
266
- mobilePrimary: getComputedStyle(document.documentElement).getPropertyValue('--adm-color-primary'),
267
- }));
268
- const baseline = {
269
- background: 'rgb(248, 249, 250)', color: 'rgb(31, 32, 33)',
270
- fontSize: '17px', fontFamily: 'monospace', link: 'rgb(101, 42, 140)', mobilePrimary: '',
271
- };
272
- expect(await outsideStyles()).toEqual(baseline);
273
- await page.getByRole('textbox', { name: '平台移动输入' }).fill('弹层继承当前页面');
274
- await page.getByRole('button', { name: '打开移动弹层' }).click();
275
- const popup = page.locator('.oxa-mobile-scope .adm-popup-body');
276
- await expect(popup).toBeVisible();
277
- await expect(popup).toContainText('弹层继承当前页面');
278
- await expect(popup).toHaveCSS('background-color', 'rgb(255, 255, 255)');
279
- expect(await outsideStyles()).toEqual(baseline);
280
- await popup.getByRole('button', { name: '关闭弹层' }).click();
281
- await expect(popup).toBeHidden();
282
- expect(await outsideStyles()).toEqual(baseline);
283
- expect(pageErrors).toEqual([]);
284
- });
285
- });
@@ -1,96 +0,0 @@
1
- import { expect, test, type Page } from '@playwright/test';
2
-
3
- const field = (page: Page, name: string) => page.locator(`[data-field-code="${name}"]`);
4
- const file = (name: string) => ({ name, mimeType: 'text/plain', buffer: Buffer.from('acceptance') });
5
- async function submit(page: Page) {
6
- await page.getByRole('button', { name: '验证表单', exact: true }).click();
7
- await expect(page.locator('[data-saved-values]')).not.toBeEmpty();
8
- return JSON.parse(await page.locator('[data-saved-values]').innerText());
9
- }
10
-
11
- test.describe('mobile managed files', () => {
12
- test.beforeEach(async ({ page }) => {
13
- await page.setViewportSize({ width: 390, height: 844 });
14
- await page.route('**/china-divisions/**', route => route.fulfill({ json: [] }));
15
- await page.route('**/files/*/content?**', route => route.fulfill({ contentType: 'image/svg+xml', body:
16
- '<svg xmlns="http://www.w3.org/2000/svg" width="640" height="480" viewBox="0 0 640 480"><rect width="640" height="480" fill="#f1f3f5"/><circle cx="470" cy="120" r="45" fill="#c6d3df"/><path d="M0 480L220 180L410 480M230 480L450 250L640 480" fill="#95a9bb"/><text x="30" y="50" font-family="sans-serif" font-size="24" fill="#34495e">Image preview fixture</text></svg>' }));
17
- });
18
-
19
- test('failed uploads retry, removed pending results stay removed and limits do not truncate', async ({ page }, info) => {
20
- const errors: string[] = [];
21
- page.on('pageerror', error => errors.push(error.message));
22
- let attempts = 0;
23
- let release!: () => void;
24
- const held = new Promise<void>(resolve => { release = resolve; });
25
- await page.route('**/__field-upload-fixture', async route => {
26
- const name = decodeURIComponent(route.request().headers()['x-file-name']);
27
- if (name === '待移除.txt') await held;
28
- if (name === '重试.txt' && ++attempts === 1) return route.fulfill({ status: 503, body: '网络暂不可用,请重试。已填写内容会保留。' });
29
- await route.fulfill({ status: 204 });
30
- });
31
- await page.goto('/field-protocol.e2e.html?mode=mobile&controlledUploads=1');
32
- const attachment = field(page, '附件');
33
- await attachment.locator('input[type=file]').setInputFiles(file('重试.txt'));
34
- await expect(attachment.getByRole('alert')).toContainText('网络暂不可用');
35
- await expect(attachment.locator('.ant-btn')).toHaveCount(0);
36
- await expect(attachment.locator('input[type=file]')).toBeHidden();
37
- await attachment.scrollIntoViewIfNeeded();
38
- await page.screenshot({ path: info.outputPath('mobile-file-retry.png') });
39
- await attachment.getByRole('button', { name: '重试', exact: true }).click();
40
- await expect(attachment.getByRole('button', { name: '预览重试.txt', exact: true })).toBeVisible();
41
- expect(attempts).toBe(2);
42
- await attachment.locator('input[type=file]').setInputFiles(file('待移除.txt'));
43
- const pending = attachment.locator('.oxa-mobile-upload-card');
44
- await expect(pending).toContainText('上传中');
45
- await pending.getByRole('button', { name: '移除', exact: true }).click();
46
- release();
47
- await expect(attachment.locator('.oxa-mobile-upload-card')).toHaveCount(0);
48
- await attachment.locator('input[type=file]').setInputFiles([file('超额一.txt'), file('超额二.txt')]);
49
- await expect(attachment.getByRole('alert')).toContainText('还可选择 1 个文件');
50
- const saved = await submit(page);
51
- expect(saved['附件'].map((item: { name: string }) => item.name)).toEqual(['验收附件.pdf', '重试.txt']);
52
- await page.getByRole('button', { name: '重新载入已保存值' }).click();
53
- await expect(attachment.getByRole('button', { name: '预览重试.txt', exact: true })).toBeVisible();
54
- await attachment.getByRole('button', { name: '移除重试.txt', exact: true }).click();
55
- expect((await submit(page))['附件']).toHaveLength(1);
56
- expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true);
57
- expect(errors).toEqual([]);
58
- });
59
-
60
- test('mobile image preview, close and attachment download use existing file APIs', async ({ page }, info) => {
61
- const errors: string[] = [];
62
- page.on('pageerror', error => errors.push(error.message));
63
- await page.route('**/files/*/preview?**', route => route.fulfill({ json: {
64
- canPreview: true, previewType: 'image', file: { name: '验收图片.png' },
65
- } }));
66
- await page.goto('/field-protocol.e2e.html?mode=mobile');
67
- const images = field(page, '图片');
68
- await expect(images.getByRole('button', { name: '上传图片', exact: true })).toBeVisible();
69
- await images.locator('input[type=file]').setInputFiles({ name: '第二张.png', mimeType: 'image/png', buffer: Buffer.from('fixture-image') });
70
- await expect(images.getByRole('button', { name: '预览第二张.png', exact: true })).toBeVisible();
71
- await expect(images.getByRole('button', { name: '预览验收图片.png', exact: true })).toBeVisible();
72
- await images.getByRole('button', { name: '预览第二张.png', exact: true }).click();
73
- await expect(page.getByRole('button', { name: '关闭预览', exact: true })).toBeVisible();
74
- await expect(page.locator('.ant-image-preview')).toHaveCount(0);
75
- await expect(page.locator('.adm-image-viewer-indicator')).toHaveText('2 / 2');
76
- await expect(page.locator('.adm-image-viewer-image-wrapper img').last()).toHaveJSProperty('naturalWidth', 640);
77
- await page.screenshot({ path: info.outputPath('mobile-image-preview.png') });
78
- const imageDownload = page.waitForEvent('download');
79
- await page.getByRole('button', { name: '下载', exact: true }).click();
80
- expect((await imageDownload).suggestedFilename()).toBe('第二张.png');
81
- await page.getByRole('button', { name: '关闭预览', exact: true }).click();
82
- await expect(page.getByRole('button', { name: '关闭预览', exact: true })).toHaveCount(0);
83
- let downloadAttempts = 0;
84
- await page.route('**/files/11111111-1111-4111-8111-111111111111/content?**', route => {
85
- downloadAttempts += 1;
86
- return downloadAttempts === 1 ? route.fulfill({ status: 503, body: 'Unavailable' }) : route.fulfill({ body: 'download acceptance', contentType: 'application/pdf' });
87
- });
88
- await field(page, '附件').getByRole('button', { name: '下载验收附件.pdf', exact: true }).click();
89
- await expect(page.getByText('FILE_CONTENT_READ_FAILED: HTTP_503', { exact: true })).toBeVisible();
90
- await expect(field(page, '附件').getByRole('button', { name: '预览验收附件.pdf', exact: true })).toBeVisible();
91
- const download = page.waitForEvent('download');
92
- await field(page, '附件').getByRole('button', { name: '下载验收附件.pdf', exact: true }).click();
93
- expect((await download).suggestedFilename()).toBe('验收附件.pdf');
94
- expect(errors).toEqual([]);
95
- });
96
- });