cabloy 5.1.158 → 5.1.160
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/.cabloy-version +1 -1
- package/.claude/skills/cabloy-contract-loop/SKILL.md +24 -7
- package/.claude/skills/cabloy-contract-loop/evals/evals.json +12 -0
- package/.claude/skills/cabloy-contract-loop/references/contract-loop-map.md +28 -0
- package/.claude/skills/cabloy-contract-loop/references/verification-checklist.md +19 -2
- package/.claude/skills/cabloy-resource-field-update/SKILL.md +18 -0
- package/.claude/skills/cabloy-zova-source-reading/SKILL.md +14 -0
- package/.claude/skills/cabloy-zova-source-reading/references/core-reading-paths.md +10 -0
- package/.github/workflows/docs-pages.yml +18 -2
- package/CHANGELOG.md +31 -0
- package/package.json +2 -1
- package/repo-docs/.vitepress/config.mjs +45 -4
- package/repo-docs/.vitepress/theme/components/GitHubRepositoriesNav.vue +255 -0
- package/repo-docs/.vitepress/theme/custom.css +21 -0
- package/repo-docs/.vitepress/theme/index.js +7 -1
- package/repo-docs/backend/department-management.md +98 -0
- package/repo-docs/backend/menu-authorization.md +124 -0
- package/repo-docs/backend/rbac-authorization.md +117 -0
- package/repo-docs/backend/resource-field-update.md +49 -2
- package/repo-docs/backend/role-management.md +92 -0
- package/repo-docs/backend/shared-rbac-architecture.md +339 -0
- package/repo-docs/backend/user-management.md +92 -0
- package/repo-docs/blogs/index.md +2 -0
- package/repo-docs/frontend/form-layout-guide.md +6 -6
- package/repo-docs/frontend/model-resource-owner-pattern.md +2 -0
- package/repo-docs/frontend/page-guide.md +12 -0
- package/repo-docs/frontend/page-meta-guide.md +27 -0
- package/repo-docs/frontend/permission-formscene-action-visibility-guide.md +5 -3
- package/repo-docs/frontend/ssr-architecture-overview.md +4 -0
- package/repo-docs/frontend/ssr-build-deploy-guide.md +1 -1
- package/repo-docs/frontend/table-action-visibility-permission-flow-guide.md +2 -0
- package/repo-docs/frontend/table-guide.md +75 -22
- package/repo-docs/frontend/table-resource-crud-cookbook.md +25 -4
- package/repo-docs/frontend/zova-form-source-reading-map.md +2 -2
- package/repo-docs/frontend/zova-form-under-the-hood.md +1 -1
- package/repo-docs/frontend/zova-reactivity-under-the-hood.md +65 -9
- package/repo-docs/frontend/zova-table-source-reading-map.md +54 -19
- package/repo-docs/frontend/zova-table-under-the-hood.md +70 -39
- package/repo-docs/fullstack/contract-loop-playbook.md +2 -0
- package/repo-docs/fullstack/ssr-site-and-flavor-setup.md +198 -0
- package/repo-docs/fullstack/vona-zova-integration.md +2 -0
- package/repo-e2e/docs/playwright.config.ts +37 -0
- package/repo-e2e/docs/specs/blogs-index.spec.ts +180 -0
- package/repo-e2e/docs/specs/github-repositories-nav.spec.ts +69 -0
- package/repo-e2e/docs/test-results/.last-run.json +4 -0
- package/repo-e2e/specs/cabloy-basic.spec.ts +136 -2
- package/test-results/.last-run.json +20 -2
- package/test-results/a-commerce-ATP-ADDR-01-aut-11dea-ss-through-Web-self-service/error-context.md +238 -0
- package/test-results/a-commerce-ATP-SPC-01-Coup-ef380-mantic-Admin-field-controls/error-context.md +240 -0
- package/test-results/a-commerce-ATP-SPC-02-Cate-9f120-on-and-publication-controls/error-context.md +238 -0
- package/test-results/a-commerce-ATP-SPC-02-Prod-5f92a-on-and-publication-controls/error-context.md +238 -0
- package/test-results/a-commerce-ATP-SPC-02-SKU--2e61e-ency-and-lifecycle-controls/error-context.md +238 -0
- package/test-results/a-commerce-ATP-SPC-04-Stoc-fa895--readonly-and-mutation-free/error-context.md +235 -0
- package/test-results/a-commerce-ATP-SPC-05-syst-fb406-e-without-mutation-controls/error-context.md +238 -0
- package/test-results/a-commerce-Commerce-sessio-4e963-ie-selects-raw-server-theme/error-context.md +226 -0
- package/test-results/a-commerce-Commerce-theme--062fe--without-hydration-mismatch/error-context.md +229 -0
- package/test-results/a-commerce-PayPal-browser--8672c-t-or-open-an-awaiting-order/error-context.md +238 -0
- package/test-results/a-commerce-Payment-callbac-f56c2--reconciles-after-hydration/error-context.md +238 -0
- package/test-results/a-commerce-Payment-cancell-3fc74-ified-provider-confirmation/error-context.md +238 -0
- package/test-results/a-commerce-Phase-50-60-aut-71d61--observes-operator-shipment/error-context.md +238 -0
- package/test-results/a-commerce-Phase-60-custom-407cd-ecutes-a-whole-order-refund/error-context.md +238 -0
- package/test-results/cabloy-basic-ATP-BASIC-TAB-e7253-and-pins-configured-columns/error-context.md +240 -0
- package/test-results/markdown-ATP-SPC-02-Produc-9d5c2-link-toolbar-edits-Markdown/error-context.md +211 -0
- package/test-results/markdown-ATP-SPC-02-Produc-9d6ea-t-editor-and-saves-Markdown/error-context.md +238 -0
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/entity/{{resourceName}}.tsx_ +1 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/entity/{{resourceName}}.tsx_ +1 -1
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/pnpm-lock.yaml +79 -52
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/config/locale/en-us.ts +3 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/config/locale/zh-cn.ts +3 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productCreate.tsx +2 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productUpdate.tsx +2 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productView.tsx +2 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordCreate.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordSelectReq.tsx +3 -7
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordUpdate.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordView.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/entity/record.tsx +8 -0
- package/vona/src/suite/a-training/modules/training-record/test/record.test.ts +51 -0
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +3 -0
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +3 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentCreate.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectResItem.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentUpdate.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentView.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/entity/student.tsx +10 -1
- package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +99 -1
- package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/package.json +1 -1
- package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-web/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-web/src/bean/pipe.filter.ts +85 -57
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/packages-cli/cli/package.json +2 -2
- package/zova/packages-cli/cli-set-front/cli/templates/rest/render.ts +2 -0
- package/zova/packages-cli/cli-set-front/cli/templates/rest/rest.ts +13 -3
- package/zova/packages-cli/cli-set-front/package.json +1 -1
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/pnpm-lock.yaml +16 -16
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx +31 -11
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controller.tsx +3 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/component/blockPageEntry/controller.tsx +1 -1
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/table/render.tsx +68 -8
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +4 -0
- package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/modules/a-form/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/lib/formLayout.ts +0 -6
- package/zova/src/suite-vendor/a-zova/modules/a-form/test/lib/formLayout.test.ts +16 -9
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/model/sdk.ts +4 -0
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/rest.ts +13 -2
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/schema.ts +1 -0
- package/zova/src/suite-vendor/a-zova/modules/a-table/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/controller.tsx +50 -0
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/render.tsx +60 -7
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/types/table.ts +6 -4
- package/zova/src/suite-vendor/a-zova/package.json +4 -4
package/test-results/a-commerce-ATP-SPC-02-Prod-5f92a-on-and-publication-controls/error-context.md
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# Instructions
|
|
2
|
+
|
|
3
|
+
- Following Playwright test failed.
|
|
4
|
+
- Explain why, be concise, respect Playwright best practices.
|
|
5
|
+
- Provide a snippet of code with the fix, if possible.
|
|
6
|
+
|
|
7
|
+
# Test info
|
|
8
|
+
|
|
9
|
+
- Name: a-commerce.spec.ts >> ATP-SPC-02: Product renders semantic Admin relation and publication controls
|
|
10
|
+
- Location: repo-e2e/specs/a-commerce.spec.ts:1643:1
|
|
11
|
+
|
|
12
|
+
# Error details
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
Error: expect(locator).toBeVisible() failed
|
|
16
|
+
|
|
17
|
+
Locator: getByRole('group', { name: 'Category' }).getByRole('combobox')
|
|
18
|
+
Expected: visible
|
|
19
|
+
Timeout: 5000ms
|
|
20
|
+
Error: element(s) not found
|
|
21
|
+
|
|
22
|
+
Call log:
|
|
23
|
+
- Expect "toBeVisible" with timeout 5000ms
|
|
24
|
+
- waiting for getByRole('group', { name: 'Category' }).getByRole('combobox')
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```yaml
|
|
29
|
+
- text: 403 Access denied.
|
|
30
|
+
- button "Go Home"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
# Test source
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
1582 | await expect(parentPicker).toBeVisible();
|
|
37
|
+
1583 | await parentPicker.selectOption({ label: parentName });
|
|
38
|
+
1584 | const parentValue = await parentPicker.inputValue();
|
|
39
|
+
1585 | expect(parentValue).toBe(String(parentId));
|
|
40
|
+
1586 | const childPublication = adminPage
|
|
41
|
+
1587 | .getByRole('group', { name: 'Published' })
|
|
42
|
+
1588 | .getByRole('combobox');
|
|
43
|
+
1589 | await childPublication.selectOption({ label: 'Published' });
|
|
44
|
+
1590 | await expect(childPublication).toHaveValue('true');
|
|
45
|
+
1591 |
|
|
46
|
+
1592 | const childCreateResponse = await adminPage.request.post(categoryActionPath, {
|
|
47
|
+
1593 | data: { name: childName, parentId: parentValue, published: true },
|
|
48
|
+
1594 | headers,
|
|
49
|
+
1595 | });
|
|
50
|
+
1596 | expect(childCreateResponse.ok()).toBeTruthy();
|
|
51
|
+
1597 | childId = (await childCreateResponse.json()).data;
|
|
52
|
+
1598 | expectTableIdentity(childId);
|
|
53
|
+
1599 |
|
|
54
|
+
1600 | await adminPage.goto(categoryListPath, { waitUntil: 'load' });
|
|
55
|
+
1601 | const childRow = adminPage.getByRole('row', { name: new RegExp(childName) });
|
|
56
|
+
1602 | await expect(childRow).toBeVisible();
|
|
57
|
+
1603 | await expect(childRow.getByText(parentName, { exact: true })).toBeVisible();
|
|
58
|
+
1604 | await expect(childRow.getByText('Published', { exact: true })).toBeVisible();
|
|
59
|
+
1605 |
|
|
60
|
+
1606 | await adminPage.goto(`${categoryListPath}/${parentId}/edit`, { waitUntil: 'load' });
|
|
61
|
+
1607 | await expect(
|
|
62
|
+
1608 | adminPage.getByRole('group', { name: 'Published' }).getByRole('combobox'),
|
|
63
|
+
1609 | ).toHaveValue('false');
|
|
64
|
+
1610 |
|
|
65
|
+
1611 | await adminPage.goto(`${categoryListPath}/${childId}/edit`, { waitUntil: 'load' });
|
|
66
|
+
1612 | await expect(
|
|
67
|
+
1613 | adminPage.getByRole('group', { name: 'Published' }).getByRole('combobox'),
|
|
68
|
+
1614 | ).toHaveValue('true');
|
|
69
|
+
1615 |
|
|
70
|
+
1616 | await adminPage.goto(`${categoryListPath}/${childId}`, { waitUntil: 'load' });
|
|
71
|
+
1617 | await expect(
|
|
72
|
+
1618 | adminPage.getByRole('group', { name: 'Parent category' }).getByRole('textbox'),
|
|
73
|
+
1619 | ).toHaveValue(parentName);
|
|
74
|
+
1620 | await expect(
|
|
75
|
+
1621 | adminPage.getByRole('group', { name: 'Published' }).getByRole('textbox'),
|
|
76
|
+
1622 | ).toHaveValue('Published');
|
|
77
|
+
1623 | await expect(adminPage.getByRole('button', { name: 'Submit', exact: true })).toHaveCount(0);
|
|
78
|
+
1624 | expect(adminPageErrors).toEqual([]);
|
|
79
|
+
1625 | } finally {
|
|
80
|
+
1626 | if (childId && headers) {
|
|
81
|
+
1627 | const response = await adminPage.request.delete(`${categoryActionPath}/${childId}`, {
|
|
82
|
+
1628 | headers,
|
|
83
|
+
1629 | });
|
|
84
|
+
1630 | expect(response.ok()).toBeTruthy();
|
|
85
|
+
1631 | }
|
|
86
|
+
1632 | if (parentId && headers) {
|
|
87
|
+
1633 | const response = await adminPage.request.delete(`${categoryActionPath}/${parentId}`, {
|
|
88
|
+
1634 | headers,
|
|
89
|
+
1635 | });
|
|
90
|
+
1636 | expect(response.ok()).toBeTruthy();
|
|
91
|
+
1637 | }
|
|
92
|
+
1638 | await adminContext.close().catch(() => {});
|
|
93
|
+
1639 | }
|
|
94
|
+
1640 | },
|
|
95
|
+
1641 | );
|
|
96
|
+
1642 |
|
|
97
|
+
1643 | test(
|
|
98
|
+
1644 | 'ATP-SPC-02: Product renders semantic Admin relation and publication controls',
|
|
99
|
+
1645 | { tag: ['@admin', '@flow', '@product'] },
|
|
100
|
+
1646 | async ({ browser }, testInfo) => {
|
|
101
|
+
1647 | test.setTimeout(60_000);
|
|
102
|
+
1648 | const suffix = `${testInfo.workerIndex}-${testInfo.parallelIndex ?? testInfo.retry}-${Date.now()}`;
|
|
103
|
+
1649 | const categoryName = `E2E Product Category ${suffix}`;
|
|
104
|
+
1650 | const productTitle = `E2E Product ${suffix}`;
|
|
105
|
+
1651 | const adminContext = await browser.newContext();
|
|
106
|
+
1652 | const adminPage = await adminContext.newPage();
|
|
107
|
+
1653 | const adminPageErrors = collectPageErrors(adminPage);
|
|
108
|
+
1654 | const productCreatePath = '/commerce-admin/rest/resource/commerce-catalog%3Aproduct/create';
|
|
109
|
+
1655 | const productListPath = '/commerce-admin/rest/resource/commerce-catalog%3Aproduct';
|
|
110
|
+
1656 | const categoryActionPath = '/api/commerce/catalog/category';
|
|
111
|
+
1657 | const productActionPath = '/api/commerce/catalog/product';
|
|
112
|
+
1658 | let categoryId: number | string | undefined;
|
|
113
|
+
1659 | let productId: number | string | undefined;
|
|
114
|
+
1660 | let headers: { Authorization: string } | undefined;
|
|
115
|
+
1661 | try {
|
|
116
|
+
1662 | await adminPage.setViewportSize({ width: 1440, height: 900 });
|
|
117
|
+
1663 | await login(adminPage, '/commerce-admin/', 'admin', '123456', 'commerceAdmin');
|
|
118
|
+
1664 | const accessToken = (await adminContext.cookies()).find(
|
|
119
|
+
1665 | cookie => cookie.name === 'token',
|
|
120
|
+
1666 | )?.value;
|
|
121
|
+
1667 | expect(accessToken).toBeTruthy();
|
|
122
|
+
1668 | headers = { Authorization: `Bearer ${accessToken}` };
|
|
123
|
+
1669 |
|
|
124
|
+
1670 | const categoryResponse = await adminPage.request.post(categoryActionPath, {
|
|
125
|
+
1671 | data: { name: categoryName, published: true },
|
|
126
|
+
1672 | headers,
|
|
127
|
+
1673 | });
|
|
128
|
+
1674 | expect(categoryResponse.ok()).toBeTruthy();
|
|
129
|
+
1675 | categoryId = (await categoryResponse.json()).data;
|
|
130
|
+
1676 | expectTableIdentity(categoryId);
|
|
131
|
+
1677 |
|
|
132
|
+
1678 | await adminPage.goto(productCreatePath, { waitUntil: 'load' });
|
|
133
|
+
1679 | const categoryPicker = adminPage
|
|
134
|
+
1680 | .getByRole('group', { name: 'Category' })
|
|
135
|
+
1681 | .getByRole('combobox');
|
|
136
|
+
> 1682 | await expect(categoryPicker).toBeVisible();
|
|
137
|
+
| ^ Error: expect(locator).toBeVisible() failed
|
|
138
|
+
1683 | await categoryPicker.selectOption({ label: categoryName });
|
|
139
|
+
1684 | await expect(categoryPicker).toHaveValue(String(categoryId));
|
|
140
|
+
1685 | const publication = adminPage.getByRole('group', { name: 'Published' }).getByRole('combobox');
|
|
141
|
+
1686 | await expect(publication).toBeVisible();
|
|
142
|
+
1687 | await publication.selectOption({ label: 'Unpublished' });
|
|
143
|
+
1688 | await expect(publication).toHaveValue('false');
|
|
144
|
+
1689 |
|
|
145
|
+
1690 | const productResponse = await adminPage.request.post(productActionPath, {
|
|
146
|
+
1691 | data: { categoryId, title: productTitle, published: false },
|
|
147
|
+
1692 | headers,
|
|
148
|
+
1693 | });
|
|
149
|
+
1694 | expect(productResponse.ok()).toBeTruthy();
|
|
150
|
+
1695 | productId = (await productResponse.json()).data;
|
|
151
|
+
1696 | expectTableIdentity(productId);
|
|
152
|
+
1697 |
|
|
153
|
+
1698 | await adminPage.goto(productListPath, { waitUntil: 'load' });
|
|
154
|
+
1699 | const productRow = adminPage.getByRole('row', { name: new RegExp(productTitle) });
|
|
155
|
+
1700 | await expect(productRow).toBeVisible();
|
|
156
|
+
1701 | await expect(productRow.getByText(categoryName, { exact: true })).toBeVisible();
|
|
157
|
+
1702 | await expect(productRow.getByText('Unpublished', { exact: true })).toBeVisible();
|
|
158
|
+
1703 |
|
|
159
|
+
1704 | await adminPage.goto(`${productListPath}/${productId}`, { waitUntil: 'load' });
|
|
160
|
+
1705 | await expect(
|
|
161
|
+
1706 | adminPage.getByRole('group', { name: 'Category' }).getByRole('textbox'),
|
|
162
|
+
1707 | ).toHaveValue(categoryName);
|
|
163
|
+
1708 | await expect(
|
|
164
|
+
1709 | adminPage.getByRole('group', { name: 'Published' }).getByRole('textbox'),
|
|
165
|
+
1710 | ).toHaveValue('Unpublished');
|
|
166
|
+
1711 | await expect(adminPage.getByRole('button', { name: 'Submit', exact: true })).toHaveCount(0);
|
|
167
|
+
1712 | expect(adminPageErrors).toEqual([]);
|
|
168
|
+
1713 | } finally {
|
|
169
|
+
1714 | if (productId && headers) {
|
|
170
|
+
1715 | const response = await adminPage.request.delete(`${productActionPath}/${productId}`, {
|
|
171
|
+
1716 | headers,
|
|
172
|
+
1717 | });
|
|
173
|
+
1718 | expect(response.ok()).toBeTruthy();
|
|
174
|
+
1719 | }
|
|
175
|
+
1720 | if (categoryId && headers) {
|
|
176
|
+
1721 | const response = await adminPage.request.delete(`${categoryActionPath}/${categoryId}`, {
|
|
177
|
+
1722 | headers,
|
|
178
|
+
1723 | });
|
|
179
|
+
1724 | expect(response.ok()).toBeTruthy();
|
|
180
|
+
1725 | }
|
|
181
|
+
1726 | await adminContext.close().catch(() => {});
|
|
182
|
+
1727 | }
|
|
183
|
+
1728 | },
|
|
184
|
+
1729 | );
|
|
185
|
+
1730 |
|
|
186
|
+
1731 | test(
|
|
187
|
+
1732 | 'ATP-SPC-02: SKU renders semantic Admin currency and lifecycle controls',
|
|
188
|
+
1733 | { tag: ['@admin', '@flow', '@sku'] },
|
|
189
|
+
1734 | async ({ browser }, testInfo) => {
|
|
190
|
+
1735 | test.setTimeout(60_000);
|
|
191
|
+
1736 | const suffix = `${testInfo.workerIndex}-${testInfo.parallelIndex ?? testInfo.retry}-${Date.now()}`;
|
|
192
|
+
1737 | const categoryName = `E2E SKU Category ${suffix}`;
|
|
193
|
+
1738 | const productTitle = `E2E SKU Product ${suffix}`;
|
|
194
|
+
1739 | const skuCode = `E2E-SKU-${suffix}`;
|
|
195
|
+
1740 | const stockActionPath = '/api/commerce/trade/stockBalance/adjustStock';
|
|
196
|
+
1741 | const adminContext = await browser.newContext();
|
|
197
|
+
1742 | const adminPage = await adminContext.newPage();
|
|
198
|
+
1743 | const adminPageErrors = collectPageErrors(adminPage);
|
|
199
|
+
1744 | const skuCreatePath = '/commerce-admin/rest/resource/commerce-catalog%3Asku/create';
|
|
200
|
+
1745 | const skuListPath = '/commerce-admin/rest/resource/commerce-catalog%3Asku';
|
|
201
|
+
1746 | const categoryActionPath = '/api/commerce/catalog/category';
|
|
202
|
+
1747 | const productActionPath = '/api/commerce/catalog/product';
|
|
203
|
+
1748 | const skuActionPath = '/api/commerce/catalog/sku';
|
|
204
|
+
1749 | let categoryId: number | string | undefined;
|
|
205
|
+
1750 | let productId: number | string | undefined;
|
|
206
|
+
1751 | let skuId: number | string | undefined;
|
|
207
|
+
1752 | let headers: { Authorization: string } | undefined;
|
|
208
|
+
1753 | try {
|
|
209
|
+
1754 | await adminPage.setViewportSize({ width: 1440, height: 900 });
|
|
210
|
+
1755 | await login(adminPage, '/commerce-admin/', 'admin', '123456', 'commerceAdmin');
|
|
211
|
+
1756 | const accessToken = (await adminContext.cookies()).find(
|
|
212
|
+
1757 | cookie => cookie.name === 'token',
|
|
213
|
+
1758 | )?.value;
|
|
214
|
+
1759 | expect(accessToken).toBeTruthy();
|
|
215
|
+
1760 | headers = { Authorization: `Bearer ${accessToken}` };
|
|
216
|
+
1761 |
|
|
217
|
+
1762 | const categoryResponse = await adminPage.request.post(categoryActionPath, {
|
|
218
|
+
1763 | data: { name: categoryName, published: true },
|
|
219
|
+
1764 | headers,
|
|
220
|
+
1765 | });
|
|
221
|
+
1766 | expect(categoryResponse.ok()).toBeTruthy();
|
|
222
|
+
1767 | categoryId = (await categoryResponse.json()).data;
|
|
223
|
+
1768 | expectTableIdentity(categoryId);
|
|
224
|
+
1769 |
|
|
225
|
+
1770 | const productResponse = await adminPage.request.post(productActionPath, {
|
|
226
|
+
1771 | data: { categoryId, title: productTitle, published: true },
|
|
227
|
+
1772 | headers,
|
|
228
|
+
1773 | });
|
|
229
|
+
1774 | expect(productResponse.ok()).toBeTruthy();
|
|
230
|
+
1775 | productId = (await productResponse.json()).data;
|
|
231
|
+
1776 | expectTableIdentity(productId);
|
|
232
|
+
1777 |
|
|
233
|
+
1778 | await adminPage.goto(skuCreatePath, { waitUntil: 'load' });
|
|
234
|
+
1779 | const productField = adminPage.getByRole('group', { name: 'Product' });
|
|
235
|
+
1780 | await expect(productField).toBeVisible();
|
|
236
|
+
1781 | const productPicker = productField.getByRole('combobox');
|
|
237
|
+
1782 | await expect(productPicker).toBeVisible();
|
|
238
|
+
```
|
package/test-results/a-commerce-ATP-SPC-02-SKU--2e61e-ency-and-lifecycle-controls/error-context.md
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# Instructions
|
|
2
|
+
|
|
3
|
+
- Following Playwright test failed.
|
|
4
|
+
- Explain why, be concise, respect Playwright best practices.
|
|
5
|
+
- Provide a snippet of code with the fix, if possible.
|
|
6
|
+
|
|
7
|
+
# Test info
|
|
8
|
+
|
|
9
|
+
- Name: a-commerce.spec.ts >> ATP-SPC-02: SKU renders semantic Admin currency and lifecycle controls
|
|
10
|
+
- Location: repo-e2e/specs/a-commerce.spec.ts:1731:1
|
|
11
|
+
|
|
12
|
+
# Error details
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
Error: expect(locator).toBeVisible() failed
|
|
16
|
+
|
|
17
|
+
Locator: getByRole('group', { name: 'Product' })
|
|
18
|
+
Expected: visible
|
|
19
|
+
Timeout: 5000ms
|
|
20
|
+
Error: element(s) not found
|
|
21
|
+
|
|
22
|
+
Call log:
|
|
23
|
+
- Expect "toBeVisible" with timeout 5000ms
|
|
24
|
+
- waiting for getByRole('group', { name: 'Product' })
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```yaml
|
|
29
|
+
- text: 403 Access denied.
|
|
30
|
+
- button "Go Home"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
# Test source
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
1680 | .getByRole('group', { name: 'Category' })
|
|
37
|
+
1681 | .getByRole('combobox');
|
|
38
|
+
1682 | await expect(categoryPicker).toBeVisible();
|
|
39
|
+
1683 | await categoryPicker.selectOption({ label: categoryName });
|
|
40
|
+
1684 | await expect(categoryPicker).toHaveValue(String(categoryId));
|
|
41
|
+
1685 | const publication = adminPage.getByRole('group', { name: 'Published' }).getByRole('combobox');
|
|
42
|
+
1686 | await expect(publication).toBeVisible();
|
|
43
|
+
1687 | await publication.selectOption({ label: 'Unpublished' });
|
|
44
|
+
1688 | await expect(publication).toHaveValue('false');
|
|
45
|
+
1689 |
|
|
46
|
+
1690 | const productResponse = await adminPage.request.post(productActionPath, {
|
|
47
|
+
1691 | data: { categoryId, title: productTitle, published: false },
|
|
48
|
+
1692 | headers,
|
|
49
|
+
1693 | });
|
|
50
|
+
1694 | expect(productResponse.ok()).toBeTruthy();
|
|
51
|
+
1695 | productId = (await productResponse.json()).data;
|
|
52
|
+
1696 | expectTableIdentity(productId);
|
|
53
|
+
1697 |
|
|
54
|
+
1698 | await adminPage.goto(productListPath, { waitUntil: 'load' });
|
|
55
|
+
1699 | const productRow = adminPage.getByRole('row', { name: new RegExp(productTitle) });
|
|
56
|
+
1700 | await expect(productRow).toBeVisible();
|
|
57
|
+
1701 | await expect(productRow.getByText(categoryName, { exact: true })).toBeVisible();
|
|
58
|
+
1702 | await expect(productRow.getByText('Unpublished', { exact: true })).toBeVisible();
|
|
59
|
+
1703 |
|
|
60
|
+
1704 | await adminPage.goto(`${productListPath}/${productId}`, { waitUntil: 'load' });
|
|
61
|
+
1705 | await expect(
|
|
62
|
+
1706 | adminPage.getByRole('group', { name: 'Category' }).getByRole('textbox'),
|
|
63
|
+
1707 | ).toHaveValue(categoryName);
|
|
64
|
+
1708 | await expect(
|
|
65
|
+
1709 | adminPage.getByRole('group', { name: 'Published' }).getByRole('textbox'),
|
|
66
|
+
1710 | ).toHaveValue('Unpublished');
|
|
67
|
+
1711 | await expect(adminPage.getByRole('button', { name: 'Submit', exact: true })).toHaveCount(0);
|
|
68
|
+
1712 | expect(adminPageErrors).toEqual([]);
|
|
69
|
+
1713 | } finally {
|
|
70
|
+
1714 | if (productId && headers) {
|
|
71
|
+
1715 | const response = await adminPage.request.delete(`${productActionPath}/${productId}`, {
|
|
72
|
+
1716 | headers,
|
|
73
|
+
1717 | });
|
|
74
|
+
1718 | expect(response.ok()).toBeTruthy();
|
|
75
|
+
1719 | }
|
|
76
|
+
1720 | if (categoryId && headers) {
|
|
77
|
+
1721 | const response = await adminPage.request.delete(`${categoryActionPath}/${categoryId}`, {
|
|
78
|
+
1722 | headers,
|
|
79
|
+
1723 | });
|
|
80
|
+
1724 | expect(response.ok()).toBeTruthy();
|
|
81
|
+
1725 | }
|
|
82
|
+
1726 | await adminContext.close().catch(() => {});
|
|
83
|
+
1727 | }
|
|
84
|
+
1728 | },
|
|
85
|
+
1729 | );
|
|
86
|
+
1730 |
|
|
87
|
+
1731 | test(
|
|
88
|
+
1732 | 'ATP-SPC-02: SKU renders semantic Admin currency and lifecycle controls',
|
|
89
|
+
1733 | { tag: ['@admin', '@flow', '@sku'] },
|
|
90
|
+
1734 | async ({ browser }, testInfo) => {
|
|
91
|
+
1735 | test.setTimeout(60_000);
|
|
92
|
+
1736 | const suffix = `${testInfo.workerIndex}-${testInfo.parallelIndex ?? testInfo.retry}-${Date.now()}`;
|
|
93
|
+
1737 | const categoryName = `E2E SKU Category ${suffix}`;
|
|
94
|
+
1738 | const productTitle = `E2E SKU Product ${suffix}`;
|
|
95
|
+
1739 | const skuCode = `E2E-SKU-${suffix}`;
|
|
96
|
+
1740 | const stockActionPath = '/api/commerce/trade/stockBalance/adjustStock';
|
|
97
|
+
1741 | const adminContext = await browser.newContext();
|
|
98
|
+
1742 | const adminPage = await adminContext.newPage();
|
|
99
|
+
1743 | const adminPageErrors = collectPageErrors(adminPage);
|
|
100
|
+
1744 | const skuCreatePath = '/commerce-admin/rest/resource/commerce-catalog%3Asku/create';
|
|
101
|
+
1745 | const skuListPath = '/commerce-admin/rest/resource/commerce-catalog%3Asku';
|
|
102
|
+
1746 | const categoryActionPath = '/api/commerce/catalog/category';
|
|
103
|
+
1747 | const productActionPath = '/api/commerce/catalog/product';
|
|
104
|
+
1748 | const skuActionPath = '/api/commerce/catalog/sku';
|
|
105
|
+
1749 | let categoryId: number | string | undefined;
|
|
106
|
+
1750 | let productId: number | string | undefined;
|
|
107
|
+
1751 | let skuId: number | string | undefined;
|
|
108
|
+
1752 | let headers: { Authorization: string } | undefined;
|
|
109
|
+
1753 | try {
|
|
110
|
+
1754 | await adminPage.setViewportSize({ width: 1440, height: 900 });
|
|
111
|
+
1755 | await login(adminPage, '/commerce-admin/', 'admin', '123456', 'commerceAdmin');
|
|
112
|
+
1756 | const accessToken = (await adminContext.cookies()).find(
|
|
113
|
+
1757 | cookie => cookie.name === 'token',
|
|
114
|
+
1758 | )?.value;
|
|
115
|
+
1759 | expect(accessToken).toBeTruthy();
|
|
116
|
+
1760 | headers = { Authorization: `Bearer ${accessToken}` };
|
|
117
|
+
1761 |
|
|
118
|
+
1762 | const categoryResponse = await adminPage.request.post(categoryActionPath, {
|
|
119
|
+
1763 | data: { name: categoryName, published: true },
|
|
120
|
+
1764 | headers,
|
|
121
|
+
1765 | });
|
|
122
|
+
1766 | expect(categoryResponse.ok()).toBeTruthy();
|
|
123
|
+
1767 | categoryId = (await categoryResponse.json()).data;
|
|
124
|
+
1768 | expectTableIdentity(categoryId);
|
|
125
|
+
1769 |
|
|
126
|
+
1770 | const productResponse = await adminPage.request.post(productActionPath, {
|
|
127
|
+
1771 | data: { categoryId, title: productTitle, published: true },
|
|
128
|
+
1772 | headers,
|
|
129
|
+
1773 | });
|
|
130
|
+
1774 | expect(productResponse.ok()).toBeTruthy();
|
|
131
|
+
1775 | productId = (await productResponse.json()).data;
|
|
132
|
+
1776 | expectTableIdentity(productId);
|
|
133
|
+
1777 |
|
|
134
|
+
1778 | await adminPage.goto(skuCreatePath, { waitUntil: 'load' });
|
|
135
|
+
1779 | const productField = adminPage.getByRole('group', { name: 'Product' });
|
|
136
|
+
> 1780 | await expect(productField).toBeVisible();
|
|
137
|
+
| ^ Error: expect(locator).toBeVisible() failed
|
|
138
|
+
1781 | const productPicker = productField.getByRole('combobox');
|
|
139
|
+
1782 | await expect(productPicker).toBeVisible();
|
|
140
|
+
1783 | await expect(productPicker).toHaveValue('');
|
|
141
|
+
1784 | const lifecycle = adminPage
|
|
142
|
+
1785 | .getByRole('group', { name: 'SKU lifecycle' })
|
|
143
|
+
1786 | .getByRole('combobox');
|
|
144
|
+
1787 | await expect(lifecycle).toBeVisible();
|
|
145
|
+
1788 | await expect(lifecycle.locator('option')).toHaveText([
|
|
146
|
+
1789 | '',
|
|
147
|
+
1790 | 'Draft',
|
|
148
|
+
1791 | 'Active',
|
|
149
|
+
1792 | 'Inactive',
|
|
150
|
+
1793 | 'Archived',
|
|
151
|
+
1794 | ]);
|
|
152
|
+
1795 | await expect(adminPage.getByRole('group', { name: 'Price (cents)' })).toBeVisible();
|
|
153
|
+
1796 |
|
|
154
|
+
1797 | const skuResponse = await adminPage.request.post(skuActionPath, {
|
|
155
|
+
1798 | data: { code: skuCode, productId, priceCents: 1234, lifecycle: 'draft' },
|
|
156
|
+
1799 | headers,
|
|
157
|
+
1800 | });
|
|
158
|
+
1801 | expect(skuResponse.ok()).toBeTruthy();
|
|
159
|
+
1802 | skuId = (await skuResponse.json()).data;
|
|
160
|
+
1803 | expectTableIdentity(skuId);
|
|
161
|
+
1804 |
|
|
162
|
+
1805 | await adminPage.goto(skuListPath, { waitUntil: 'load' });
|
|
163
|
+
1806 | const skuRow = adminPage.getByRole('row', { name: new RegExp(skuCode) });
|
|
164
|
+
1807 | await expect(skuRow).toBeVisible();
|
|
165
|
+
1808 | await expect(skuRow.getByText('12.34', { exact: true })).toBeVisible();
|
|
166
|
+
1809 | await expect(skuRow.getByText('0', { exact: true })).toBeVisible();
|
|
167
|
+
1810 | await expect(skuRow.getByText('Draft', { exact: true })).toBeVisible();
|
|
168
|
+
1811 | await expect(skuRow.getByRole('link', { name: skuCode, exact: true })).toBeVisible();
|
|
169
|
+
1812 | const adjustStockButton = skuRow.getByRole('button', { name: 'Adjust stock', exact: true });
|
|
170
|
+
1813 | await expect(adjustStockButton).toBeVisible();
|
|
171
|
+
1814 | await expect(adminPage.locator('html')).toHaveAttribute(
|
|
172
|
+
1815 | 'data-zova-hydrated',
|
|
173
|
+
1816 | 'commerceAdmin',
|
|
174
|
+
1817 | );
|
|
175
|
+
1818 | await adjustStockButton.click();
|
|
176
|
+
1819 | await expect(adminPage.getByRole('dialog')).toBeVisible();
|
|
177
|
+
1820 | await expect(adminPage.getByRole('dialog').getByText(skuCode, { exact: true })).toBeVisible();
|
|
178
|
+
1821 | await expect(adminPage.getByRole('dialog').getByText('0', { exact: true })).toBeVisible();
|
|
179
|
+
1822 | const deltaInput = adminPage.getByRole('group', { name: 'Delta' }).getByRole('spinbutton');
|
|
180
|
+
1823 | const reasonInput = adminPage.getByRole('group', { name: 'Reason' }).getByRole('textbox');
|
|
181
|
+
1824 | await deltaInput.fill('7');
|
|
182
|
+
1825 | await reasonInput.fill('Initial E2E stock');
|
|
183
|
+
1826 | const stockRequest = adminPage.waitForRequest(request => {
|
|
184
|
+
1827 | return request.method() === 'POST' && new URL(request.url()).pathname === stockActionPath;
|
|
185
|
+
1828 | });
|
|
186
|
+
1829 | const stockResponse = adminPage.waitForResponse(response => {
|
|
187
|
+
1830 | return (
|
|
188
|
+
1831 | response.request().method() === 'POST' &&
|
|
189
|
+
1832 | new URL(response.url()).pathname === stockActionPath
|
|
190
|
+
1833 | );
|
|
191
|
+
1834 | });
|
|
192
|
+
1835 | await adminPage
|
|
193
|
+
1836 | .getByRole('dialog')
|
|
194
|
+
1837 | .getByRole('button', { name: 'Adjust stock', exact: true })
|
|
195
|
+
1838 | .click();
|
|
196
|
+
1839 | const stockRequestValue = await stockRequest;
|
|
197
|
+
1840 | expect(stockRequestValue.postDataJSON()).toMatchObject({
|
|
198
|
+
1841 | skuId,
|
|
199
|
+
1842 | delta: 7,
|
|
200
|
+
1843 | reason: 'Initial E2E stock',
|
|
201
|
+
1844 | correlationId: expect.any(String),
|
|
202
|
+
1845 | });
|
|
203
|
+
1846 | expect((await stockResponse).ok()).toBeTruthy();
|
|
204
|
+
1847 | await expect(adminPage.getByRole('dialog')).toHaveCount(0);
|
|
205
|
+
1848 | await expect(skuRow.getByText('7', { exact: true })).toBeVisible();
|
|
206
|
+
1849 | await expect(
|
|
207
|
+
1850 | adminPage.locator('section').getByText('SKU code', { exact: true }),
|
|
208
|
+
1851 | ).toBeVisible();
|
|
209
|
+
1852 | await expect(adminPage.getByText('Created At', { exact: true })).toHaveCount(1);
|
|
210
|
+
1853 | await expect(adminPage.getByRole('group', { name: 'Attributes' })).toHaveCount(0);
|
|
211
|
+
1854 |
|
|
212
|
+
1855 | await adminPage.goto(`${skuListPath}/${skuId}`, { waitUntil: 'load' });
|
|
213
|
+
1856 | await expect(
|
|
214
|
+
1857 | adminPage.getByRole('group', { name: 'SKU lifecycle' }).getByRole('textbox'),
|
|
215
|
+
1858 | ).toHaveValue('Draft');
|
|
216
|
+
1859 | await expect(adminPage.getByRole('button', { name: 'Submit', exact: true })).toHaveCount(0);
|
|
217
|
+
1860 | await expect(adminPage.getByRole('button', { name: 'Back', exact: true })).toBeVisible();
|
|
218
|
+
1861 | expect(adminPageErrors).toEqual([]);
|
|
219
|
+
1862 | } finally {
|
|
220
|
+
1863 | if (skuId && headers) {
|
|
221
|
+
1864 | await adminPage.request.delete(`${stockActionPath}/${skuId}`, { headers }).catch(() => {});
|
|
222
|
+
1865 | const response = await adminPage.request.delete(`${skuActionPath}/${skuId}`, { headers });
|
|
223
|
+
1866 | expect(response.ok()).toBeTruthy();
|
|
224
|
+
1867 | }
|
|
225
|
+
1868 | if (productId && headers) {
|
|
226
|
+
1869 | const response = await adminPage.request.delete(`${productActionPath}/${productId}`, {
|
|
227
|
+
1870 | headers,
|
|
228
|
+
1871 | });
|
|
229
|
+
1872 | expect(response.ok()).toBeTruthy();
|
|
230
|
+
1873 | }
|
|
231
|
+
1874 | if (categoryId && headers) {
|
|
232
|
+
1875 | const response = await adminPage.request.delete(`${categoryActionPath}/${categoryId}`, {
|
|
233
|
+
1876 | headers,
|
|
234
|
+
1877 | });
|
|
235
|
+
1878 | expect(response.ok()).toBeTruthy();
|
|
236
|
+
1879 | }
|
|
237
|
+
1880 | await adminContext.close().catch(() => {});
|
|
238
|
+
```
|