openxiangda-cli 2.0.0-alpha.169 → 2.0.0-alpha.170
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.
- package/package.json +5 -5
- package/template/AGENTS.md +38 -24
- package/template/README.md +14 -17
- package/template/apps/web/e2e/component-defaults-fixture.css +15 -0
- package/template/apps/web/e2e/data-api-live-fixture.tsx +3 -3
- package/template/apps/web/e2e/field-protocol-fixture.tsx +48 -11
- package/template/apps/web/e2e/field-protocol.spec.ts +90 -0
- package/template/apps/web/e2e/mobile-files.spec.ts +93 -0
- package/template/apps/web/e2e/mobile-reference-fixture.tsx +314 -0
- package/template/apps/web/e2e/mobile-reference.spec.ts +360 -0
- package/template/apps/web/e2e/mobile-selection.spec.ts +199 -0
- package/template/apps/web/e2e/resource-experience-fixture.tsx +54 -9
- package/template/apps/web/e2e/resource-platform-mock.ts +202 -0
- package/template/apps/web/e2e/resources.spec.ts +77 -240
- package/template/apps/web/e2e/standard-admin.spec.ts +441 -0
- package/template/apps/web/e2e/workflow.spec.ts +2 -2
- package/template/apps/web/field-protocol.e2e.html +7 -0
- package/template/apps/web/mobile-reference.e2e.html +27 -0
- package/template/apps/web/package.json +1 -1
- package/template/apps/web/resource-experience.e2e.html +1 -0
- package/template/apps/web/src/document.css +5 -0
- package/template/apps/web/src/main.tsx +1 -0
- package/template/apps/web/src/user-end/tailwind.css +1 -13
- package/template/apps/web/test/contracts.test.ts +7 -6
- package/template/apps/web/vite.config.ts +1 -0
- package/template/appspec/app.md +27 -1
- package/template/openxiangda.config.ts +2 -24
- package/template/package.json +3 -4
- package/template/apps/server/Dockerfile +0 -20
- package/template/apps/server/package.json +0 -27
- package/template/apps/server/src/app.module.ts +0 -23
- package/template/apps/server/src/main.ts +0 -5
- package/template/apps/server/test/smoke.test.ts +0 -17
- package/template/apps/server/tsconfig.json +0 -9
- package/template/apps/web/e2e/theme-contract-fixture.css +0 -15
- package/template/scripts/verify-template-budget.mjs +0 -49
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { expect, test
|
|
1
|
+
import { expect, test } from '@playwright/test';
|
|
2
|
+
import { mockPlatform } from './resource-platform-mock';
|
|
2
3
|
import {
|
|
3
4
|
appCode,
|
|
4
5
|
capabilities,
|
|
@@ -42,204 +43,6 @@ const semanticResourceItem = {
|
|
|
42
43
|
displayOrder: 0,
|
|
43
44
|
};
|
|
44
45
|
|
|
45
|
-
function runtimeAuthorization(authorized: boolean, superAdmin = false) {
|
|
46
|
-
return {
|
|
47
|
-
schemaVersion: 'openxiangda.runtime-authorization/v2',
|
|
48
|
-
state: 'active',
|
|
49
|
-
environment: {
|
|
50
|
-
id: 'preproduction-id',
|
|
51
|
-
key: 'preproduction',
|
|
52
|
-
activeAppVersionId: 'app-version-1',
|
|
53
|
-
headRevision: 1,
|
|
54
|
-
authzRevisionId: 'authz-1',
|
|
55
|
-
authzVersion: 1,
|
|
56
|
-
scopeDataVersion: 'scope-1',
|
|
57
|
-
},
|
|
58
|
-
subjectProfile: {
|
|
59
|
-
schemaVersion: 'openxiangda.subject-profile/v2',
|
|
60
|
-
userId: 'acceptance-user',
|
|
61
|
-
displayName: '王老师',
|
|
62
|
-
avatarUrl: null,
|
|
63
|
-
jobNumber: 'T001',
|
|
64
|
-
affiliatedDepartment: { id: 'department-1', name: '理学院' },
|
|
65
|
-
},
|
|
66
|
-
roles: [
|
|
67
|
-
{ code: 'college_admin', name: '学院管理员', source: 'package' },
|
|
68
|
-
{ code: 'instrument_admin', name: '仪器管理员', source: 'package' },
|
|
69
|
-
],
|
|
70
|
-
principal: {
|
|
71
|
-
type: 'user_union',
|
|
72
|
-
userId: 'acceptance-user',
|
|
73
|
-
roleCodes: ['college_admin', 'instrument_admin'],
|
|
74
|
-
capabilityCodes: authorized
|
|
75
|
-
? [
|
|
76
|
-
...allCapabilities,
|
|
77
|
-
'app:openxiangda-application:operations:read',
|
|
78
|
-
]
|
|
79
|
-
: [],
|
|
80
|
-
isAppSuperAdmin: superAdmin,
|
|
81
|
-
identityScope: 'user-union:preproduction-id:acceptance-user:authz-1',
|
|
82
|
-
},
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async function mockPlatform(
|
|
87
|
-
page: Page,
|
|
88
|
-
authorized = true,
|
|
89
|
-
targetRuntimeBase = runtimeBase,
|
|
90
|
-
superAdmin = false,
|
|
91
|
-
queryItemsByResource: Record<string, Array<Record<string, unknown>>> = {},
|
|
92
|
-
) {
|
|
93
|
-
let contextReads = 0;
|
|
94
|
-
let avatarCompletes = 0;
|
|
95
|
-
const observed: Array<{ path: string; perspectiveCode: string }> = [];
|
|
96
|
-
await page.route('https://upload.example.test/avatar-upload', route =>
|
|
97
|
-
route.fulfill({ status: 200, body: '' })
|
|
98
|
-
);
|
|
99
|
-
await page.route(`**${targetRuntimeBase}/**`, async route => {
|
|
100
|
-
if (route.request().resourceType() !== 'document') return route.continue();
|
|
101
|
-
const response = await route.fetch();
|
|
102
|
-
const body = (await response.text()).replace(
|
|
103
|
-
'<head>',
|
|
104
|
-
`<head><meta name="openxiangda-runtime-base" content="${targetRuntimeBase}/" />` +
|
|
105
|
-
`<meta name="openxiangda-app-code" content="${appCode}" />` +
|
|
106
|
-
'<meta name="openxiangda-environment" content="preproduction" />'
|
|
107
|
-
);
|
|
108
|
-
await route.fulfill({ response, body });
|
|
109
|
-
});
|
|
110
|
-
await page.route('**/service/**', async route => {
|
|
111
|
-
const url = new URL(route.request().url());
|
|
112
|
-
const request = route.request();
|
|
113
|
-
const perspectiveCode = request.headers()['x-openxiangda-perspective'] || '';
|
|
114
|
-
if (url.pathname.endsWith('/native/authz/current')) {
|
|
115
|
-
contextReads += 1;
|
|
116
|
-
return route.fulfill({
|
|
117
|
-
contentType: 'application/json',
|
|
118
|
-
body: JSON.stringify({
|
|
119
|
-
code: 200,
|
|
120
|
-
data: runtimeAuthorization(authorized, superAdmin),
|
|
121
|
-
}),
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
if (url.pathname.endsWith('/native/authz/profile/avatar/initiate')) {
|
|
125
|
-
return route.fulfill({
|
|
126
|
-
contentType: 'application/json',
|
|
127
|
-
body: JSON.stringify({
|
|
128
|
-
code: 200,
|
|
129
|
-
data: {
|
|
130
|
-
uploadUrl: 'https://upload.example.test/avatar-upload',
|
|
131
|
-
uploadMethod: 'put',
|
|
132
|
-
headers: { 'content-type': 'image/png' },
|
|
133
|
-
objectName: 'images/tenant/app/user/avatar.png',
|
|
134
|
-
},
|
|
135
|
-
}),
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
if (url.pathname.endsWith('/native/authz/profile/avatar/complete')) {
|
|
139
|
-
avatarCompletes += 1;
|
|
140
|
-
return route.fulfill({
|
|
141
|
-
contentType: 'application/json',
|
|
142
|
-
body: JSON.stringify({
|
|
143
|
-
code: 200,
|
|
144
|
-
data: {
|
|
145
|
-
schemaVersion: 'openxiangda.subject-profile/v2',
|
|
146
|
-
userId: 'acceptance-user',
|
|
147
|
-
displayName: '王老师',
|
|
148
|
-
avatarUrl: 'https://cdn.example.test/avatar/new.png',
|
|
149
|
-
jobNumber: 'T001',
|
|
150
|
-
affiliatedDepartment: { id: 'department-1', name: '理学院' },
|
|
151
|
-
},
|
|
152
|
-
}),
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
const sourceQuery = url.pathname.match(
|
|
156
|
-
/\/native\/data-resources\/([^/]+)\/fields\/([^/]+)\/source\/query$/
|
|
157
|
-
);
|
|
158
|
-
if (sourceQuery) {
|
|
159
|
-
observed.push({ path: url.pathname, perspectiveCode });
|
|
160
|
-
return route.fulfill({
|
|
161
|
-
contentType: 'application/json',
|
|
162
|
-
body: JSON.stringify({
|
|
163
|
-
code: 200,
|
|
164
|
-
data: {
|
|
165
|
-
schemaVersion: 'openxiangda.data-field-source-page/v2',
|
|
166
|
-
resourceCode: decodeURIComponent(sourceQuery[1]),
|
|
167
|
-
fieldCode: decodeURIComponent(sourceQuery[2]),
|
|
168
|
-
items: [],
|
|
169
|
-
nextCursor: null,
|
|
170
|
-
},
|
|
171
|
-
}),
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
const exportRequest = url.pathname.match(
|
|
175
|
-
/\/native\/data\/([^/]+)\/export$/
|
|
176
|
-
);
|
|
177
|
-
if (exportRequest) {
|
|
178
|
-
observed.push({ path: url.pathname, perspectiveCode });
|
|
179
|
-
return route.fulfill({
|
|
180
|
-
status: 200,
|
|
181
|
-
contentType: 'text/csv; charset=utf-8',
|
|
182
|
-
headers: {
|
|
183
|
-
'content-disposition': `attachment; filename="${decodeURIComponent(
|
|
184
|
-
exportRequest[1]
|
|
185
|
-
)}.csv"`,
|
|
186
|
-
},
|
|
187
|
-
body: 'id,name\nacceptance-1,OpenXiangda\n',
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
const query = url.pathname.match(/\/native\/data\/([^/]+)\/query$/);
|
|
191
|
-
if (query) {
|
|
192
|
-
const resourceCode = decodeURIComponent(query[1]);
|
|
193
|
-
const items = queryItemsByResource[resourceCode] || [];
|
|
194
|
-
observed.push({ path: url.pathname, perspectiveCode });
|
|
195
|
-
return route.fulfill({
|
|
196
|
-
contentType: 'application/json',
|
|
197
|
-
body: JSON.stringify({
|
|
198
|
-
code: 200,
|
|
199
|
-
data: {
|
|
200
|
-
schemaVersion: 'openxiangda.data-page/v2',
|
|
201
|
-
resourceCode,
|
|
202
|
-
appVersionId: 'app-version-1',
|
|
203
|
-
environmentHeadRevision: 1,
|
|
204
|
-
items,
|
|
205
|
-
total: items.length,
|
|
206
|
-
limit: 500,
|
|
207
|
-
offset: 0,
|
|
208
|
-
},
|
|
209
|
-
}),
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
if (url.pathname.includes('/directory/')) {
|
|
213
|
-
observed.push({ path: url.pathname, perspectiveCode });
|
|
214
|
-
return route.fulfill({
|
|
215
|
-
contentType: 'application/json',
|
|
216
|
-
body: JSON.stringify({ code: 200, data: { items: [], nextCursor: null } }),
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
if (url.pathname.includes('/openxiangda-app-api/v2/')) {
|
|
220
|
-
observed.push({ path: url.pathname, perspectiveCode });
|
|
221
|
-
return route.fulfill({
|
|
222
|
-
contentType: 'application/json',
|
|
223
|
-
body: JSON.stringify({ code: 200, data: { status: 'ok' } }),
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
return route.fulfill({
|
|
227
|
-
status: 404,
|
|
228
|
-
contentType: 'application/json',
|
|
229
|
-
body: JSON.stringify({ code: 404, message: 'not mocked' }),
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
return {
|
|
233
|
-
observed,
|
|
234
|
-
get contextReads() {
|
|
235
|
-
return contextReads;
|
|
236
|
-
},
|
|
237
|
-
get avatarCompletes() {
|
|
238
|
-
return avatarCompletes;
|
|
239
|
-
},
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
|
|
243
46
|
test('renders the compiled desktop resource contract and shared workbench', async ({ page }) => {
|
|
244
47
|
await mockPlatform(page);
|
|
245
48
|
if (!codes.length) {
|
|
@@ -256,19 +59,18 @@ test('renders the compiled desktop resource contract and shared workbench', asyn
|
|
|
256
59
|
await expect(page).toHaveURL(`${runtimeBase}/admin/resources/${code}`);
|
|
257
60
|
await expect(page.locator('.oxa-sider').getByText(definition.name, { exact: true })).toBeVisible();
|
|
258
61
|
await expect(page.getByRole('button', { name: /导出$/ })).toBeVisible();
|
|
259
|
-
await expect(page.getByRole('button', { name:
|
|
260
|
-
await expect(page.getByRole('button', { name: /密度$/ })).toBeVisible();
|
|
62
|
+
await expect(page.getByRole('button', { name: /显示列$/ })).toBeVisible();
|
|
261
63
|
if ((definition.surface.list?.filterFields?.length || 0) > 2) {
|
|
262
|
-
const more = page.getByRole('button', { name:
|
|
64
|
+
const more = page.getByRole('button', { name: /^筛选/ });
|
|
263
65
|
await expect(more).toBeVisible();
|
|
264
66
|
await more.click();
|
|
265
|
-
const modal = page.getByRole('dialog', { name: '
|
|
67
|
+
const modal = page.getByRole('dialog', { name: '筛选' });
|
|
266
68
|
await expect(modal).toBeVisible();
|
|
267
69
|
await expect(modal.getByRole('button', { name: '应用筛选' })).toBeVisible();
|
|
268
70
|
await expect
|
|
269
71
|
.poll(async () => (await modal.boundingBox())?.width || 0)
|
|
270
72
|
.toBeGreaterThan(700);
|
|
271
|
-
await modal.getByRole('button', { name: '
|
|
73
|
+
await modal.getByRole('button', { name: 'Close' }).click();
|
|
272
74
|
}
|
|
273
75
|
const downloadPromise = page.waitForEvent('download');
|
|
274
76
|
await page.getByRole('button', { name: /导出$/ }).click();
|
|
@@ -283,15 +85,16 @@ test('renders the compiled desktop resource contract and shared workbench', asyn
|
|
|
283
85
|
await expect(page.locator('.oxa-user-dropdown-profile')).toContainText('理学院');
|
|
284
86
|
await expect(page.locator('.oxa-user-dropdown-roles')).toContainText('学院管理员');
|
|
285
87
|
await expect(page.locator('.oxa-user-dropdown-roles')).not.toContainText('college_admin');
|
|
286
|
-
await expect(page.getByText('
|
|
287
|
-
await page.getByText('
|
|
288
|
-
await expect(page.locator('html')).toHaveAttribute('data-oxa-theme', 'dark');
|
|
289
|
-
await expect.poll(() => page.evaluate(() => localStorage.getItem('openxiangda.admin.appearance'))).toBe('dark');
|
|
88
|
+
await expect(page.getByText('外观', { exact: true })).toHaveCount(0);
|
|
89
|
+
await expect(page.getByText('跟随系统', { exact: true })).toHaveCount(0);
|
|
290
90
|
});
|
|
291
91
|
|
|
292
|
-
test('
|
|
92
|
+
test('default components ignore old preferences and leave the surrounding document unchanged', async ({ page }) => {
|
|
93
|
+
const errors: string[] = [];
|
|
94
|
+
page.on('pageerror', error => errors.push(error.message));
|
|
293
95
|
await page.emulateMedia({ colorScheme: 'dark' });
|
|
294
|
-
await
|
|
96
|
+
await page.addInitScript(() => localStorage.setItem('openxiangda.admin.appearance', 'dark'));
|
|
97
|
+
const platform = await mockPlatform(page);
|
|
295
98
|
await page.goto('/resource-experience.e2e.html');
|
|
296
99
|
await page
|
|
297
100
|
.locator('.oxa-sider')
|
|
@@ -299,23 +102,22 @@ test('custom admin pages inherit tokens and follow live system appearance', asyn
|
|
|
299
102
|
.click();
|
|
300
103
|
|
|
301
104
|
const customPage = page.getByTestId('custom-admin-page');
|
|
302
|
-
const customPanel = customPage.locator('.
|
|
105
|
+
const customPanel = customPage.locator('.component-defaults-panel');
|
|
303
106
|
await expect(customPage).toBeVisible();
|
|
304
107
|
await expect(customPanel).toBeVisible();
|
|
305
|
-
|
|
306
|
-
const readThemeColors = () =>
|
|
108
|
+
const readDefaultColors = () =>
|
|
307
109
|
page.evaluate(() => {
|
|
308
110
|
const host = document.querySelector('[data-testid="custom-admin-page"]');
|
|
309
|
-
if (!host) throw new Error('
|
|
111
|
+
if (!host) throw new Error('CUSTOM_ADMIN_COMPONENT_HOST_NOT_READY');
|
|
310
112
|
const probe = document.createElement('div');
|
|
311
113
|
probe.style.cssText = [
|
|
312
114
|
'position:fixed',
|
|
313
115
|
'width:1px',
|
|
314
116
|
'height:1px',
|
|
315
|
-
'background:var(--
|
|
316
|
-
'color:var(--
|
|
317
|
-
'border:1px solid var(--
|
|
318
|
-
'outline:1px solid var(--
|
|
117
|
+
'background:var(--ant-color-bg-layout)',
|
|
118
|
+
'color:var(--ant-color-bg-container)',
|
|
119
|
+
'border:1px solid var(--ant-color-border-secondary)',
|
|
120
|
+
'outline:1px solid var(--ant-color-text)',
|
|
319
121
|
].join(';');
|
|
320
122
|
host.appendChild(probe);
|
|
321
123
|
const styles = getComputedStyle(probe);
|
|
@@ -328,19 +130,51 @@ test('custom admin pages inherit tokens and follow live system appearance', asyn
|
|
|
328
130
|
probe.remove();
|
|
329
131
|
return result;
|
|
330
132
|
});
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
await expect(
|
|
334
|
-
await expect(customPanel).toHaveCSS('color',
|
|
335
|
-
await expect(customPanel).toHaveCSS('
|
|
133
|
+
const defaults = await readDefaultColors();
|
|
134
|
+
expect(defaults.surface).toBe('rgb(255, 255, 255)');
|
|
135
|
+
await expect(customPage).toHaveCSS('background-color', defaults.canvas);
|
|
136
|
+
await expect(customPanel).toHaveCSS('background-color', defaults.surface);
|
|
137
|
+
await expect(customPanel).toHaveCSS('color', defaults.text);
|
|
138
|
+
await expect(customPanel).toHaveCSS('border-color', defaults.border);
|
|
139
|
+
|
|
140
|
+
const hostState = () => page.evaluate(() => {
|
|
141
|
+
const host = getComputedStyle(document.getElementById('outside-ui')!);
|
|
142
|
+
return {
|
|
143
|
+
font: host.fontFamily, color: host.color, background: host.backgroundColor,
|
|
144
|
+
boxSizing: host.boxSizing,
|
|
145
|
+
documentStyle: document.documentElement.getAttribute('style'),
|
|
146
|
+
documentAttributes: [...document.documentElement.attributes].map(attribute => attribute.name),
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
const outside = await hostState();
|
|
150
|
+
expect(outside).toEqual({
|
|
151
|
+
font: 'monospace', color: 'rgb(31, 32, 33)', background: 'rgb(248, 249, 250)',
|
|
152
|
+
boxSizing: 'content-box', documentStyle: null, documentAttributes: ['lang'],
|
|
153
|
+
});
|
|
336
154
|
|
|
337
155
|
await page.emulateMedia({ colorScheme: 'light' });
|
|
338
|
-
await
|
|
339
|
-
|
|
340
|
-
await
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
await
|
|
156
|
+
expect(await readDefaultColors()).toEqual(defaults);
|
|
157
|
+
await expect(customPanel).toHaveCSS('background-color', defaults.surface);
|
|
158
|
+
expect(await hostState()).toEqual(outside);
|
|
159
|
+
|
|
160
|
+
const identityReads = platform.contextReads;
|
|
161
|
+
await page.getByRole('button', { name: '打开用户端' }).click();
|
|
162
|
+
await page.getByRole('textbox', { name: '用户端输入' }).fill('保留业务输入');
|
|
163
|
+
await page.getByRole('combobox').click();
|
|
164
|
+
const dropdown = page.locator('.ant-select-dropdown:visible');
|
|
165
|
+
await expect(dropdown).toHaveCSS('background-color', defaults.surface);
|
|
166
|
+
await dropdown.getByText('可用选项', { exact: true }).click();
|
|
167
|
+
await page.getByRole('button', { name: '打开用户弹窗' }).click();
|
|
168
|
+
const dialog = page.getByRole('dialog');
|
|
169
|
+
await expect(dialog).toBeVisible();
|
|
170
|
+
await expect(dialog.locator('.ant-modal-container')).toHaveCSS('background-color', defaults.surface);
|
|
171
|
+
await page.emulateMedia({ colorScheme: 'dark' });
|
|
172
|
+
await expect(dialog.locator('.ant-modal-container')).toHaveCSS('background-color', defaults.surface);
|
|
173
|
+
await dialog.getByRole('button', { name: '确 定' }).click();
|
|
174
|
+
await expect(page.getByRole('textbox', { name: '用户端输入' })).toHaveValue('保留业务输入');
|
|
175
|
+
expect(await hostState()).toEqual(outside);
|
|
176
|
+
expect(platform.contextReads).toBe(identityReads);
|
|
177
|
+
expect(errors).toEqual([]);
|
|
344
178
|
});
|
|
345
179
|
|
|
346
180
|
test('opens the stable admin entry under the preproduction gateway mount', async ({ page }) => {
|
|
@@ -436,7 +270,7 @@ test('mounts generated operation routes and typed actions in the unified Shell',
|
|
|
436
270
|
await page.goto('/resource-experience.e2e.html');
|
|
437
271
|
await expect(
|
|
438
272
|
page.getByRole('button', { name: '发布选中栏目(0)' })
|
|
439
|
-
).
|
|
273
|
+
).toHaveCount(0);
|
|
440
274
|
await page.locator('.oxa-sider').getByText('内容业务操作', { exact: true }).click();
|
|
441
275
|
await expect(page).toHaveURL('/admin/operations/content');
|
|
442
276
|
await expect(
|
|
@@ -557,8 +391,9 @@ test('fails closed on a direct operation route without its capability', async ({
|
|
|
557
391
|
test('keeps platform implementation guidance out of generated forms', async ({ page }) => {
|
|
558
392
|
await mockPlatform(page, true, runtimeBase, true);
|
|
559
393
|
await page.goto('/resource-experience.e2e.html');
|
|
560
|
-
await page.getByRole('button', { name: '
|
|
561
|
-
await expect(page
|
|
394
|
+
await page.getByRole('button', { name: '新增', exact: true }).click();
|
|
395
|
+
await expect(page.getByRole('dialog', { name: '新增数据' })).toBeVisible();
|
|
396
|
+
await expect(page).toHaveURL('/admin/resources/resource-01');
|
|
562
397
|
const formLabels = page.locator('.ant-form-item-label label');
|
|
563
398
|
await expect(formLabels.first()).toBeVisible();
|
|
564
399
|
const labels = await formLabels.allTextContents();
|
|
@@ -571,21 +406,23 @@ test('keeps platform implementation guidance out of generated forms', async ({ p
|
|
|
571
406
|
await expect(page.getByText('当前角色不可修改此字段', { exact: true })).toHaveCount(0);
|
|
572
407
|
});
|
|
573
408
|
|
|
574
|
-
test('keeps
|
|
409
|
+
test('keeps one toolbar and opens all declared filters on demand', async ({ page }) => {
|
|
575
410
|
await mockPlatform(page, true, runtimeBase, true);
|
|
576
411
|
await page.goto('/resource-experience.e2e.html');
|
|
577
|
-
await expect(page.locator('.oxa-
|
|
412
|
+
await expect(page.locator('.oxa-list-toolbar')).toHaveCount(1);
|
|
413
|
+
await expect(page.locator('.oxa-filter-primary-item')).toHaveCount(0);
|
|
578
414
|
await expect(
|
|
579
415
|
page.getByPlaceholder('搜索栏目名称或栏目编码')
|
|
580
416
|
).toBeVisible();
|
|
581
|
-
const more = page.getByRole('button', { name: '
|
|
417
|
+
const more = page.getByRole('button', { name: '筛选' });
|
|
582
418
|
await more.click();
|
|
583
|
-
const modal = page.getByRole('dialog', { name: '
|
|
419
|
+
const modal = page.getByRole('dialog', { name: '筛选' });
|
|
584
420
|
await expect(modal).toBeVisible();
|
|
585
|
-
await
|
|
586
|
-
await
|
|
587
|
-
await expect(
|
|
588
|
-
await expect(
|
|
421
|
+
await modal.getByRole('button', { name: '添加条件', exact: true }).click();
|
|
422
|
+
await modal.getByRole('combobox', { name: '筛选字段' }).click();
|
|
423
|
+
await expect(page.getByRole('option', { name: '栏目管理员' })).toBeVisible();
|
|
424
|
+
await expect(page.getByRole('option', { name: '配套设施' })).toBeVisible();
|
|
425
|
+
await expect(page.getByRole('option', { name: '开放星期' })).toBeVisible();
|
|
589
426
|
await expect
|
|
590
427
|
.poll(async () => (await modal.boundingBox())?.width || 0)
|
|
591
428
|
.toBeGreaterThan(700);
|