@controleonline/ui-customers 1.0.81 → 1.0.82
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/.github/workflows/integration-source-gate.yml +68 -0
- package/.github/workflows/publish-npm.yml +91 -0
- package/AGENTS.md +12 -6
- package/docs/technical/Client-Details-Criar-Usuario.md +113 -0
- package/package.json +5 -4
- package/src/react/components/tabs/AddressesTab.js +240 -335
- package/src/react/components/tabs/DocumentAttachments.js +270 -0
- package/src/react/components/tabs/DocumentsTab.js +355 -463
- package/src/react/components/tabs/DocumentsTab.styles.js +123 -0
- package/src/react/components/tabs/EmployeeCreateFormFields.js +203 -0
- package/src/react/components/tabs/EmployeesTab.js +17 -171
- package/src/react/components/tabs/EmployeesTab.styles.js +220 -172
- package/src/react/components/tabs/GeneralTab.js +84 -46
- package/src/react/components/tabs/GeneralTab.styles.js +81 -77
- package/src/react/components/tabs/MediaTab.js +50 -5
- package/src/react/components/tabs/PeopleCategoriesPanel.js +161 -78
- package/src/react/components/tabs/SalesmanTab.js +1 -1
- package/src/react/components/tabs/UserFormModal.js +26 -2
- package/src/react/components/tabs/UsersTab.js +30 -19
- package/src/react/components/tabs/UsersTab.styles.js +257 -257
- package/src/react/components/tabs/UsersTabApiKeyModal.js +115 -0
- package/src/react/components/tabs/UsersTabUserModal.js +222 -0
- package/src/react/components/tabs/addressesTabHelpers.js +190 -0
- package/src/react/components/tabs/documentsTabHelpers.js +142 -0
- package/src/react/components/tabs/employeeContacts.js +3 -23
- package/src/react/components/tabs/employeeUnlink.js +7 -4
- package/src/react/components/tabs/employeesTabHelpers.js +33 -9
- package/src/react/components/tabs/generalTabHelpers.js +30 -8
- package/src/react/components/tabs/useGeneralTabPeopleLink.js +68 -0
- package/src/react/components/tabs/usersTabHelpers.js +24 -23
- package/src/react/pages/details.page.styles.js +114 -100
- package/src/react/utils/humanCompanyLinkTypes.js +28 -0
- package/src/tests/browser/manager/client-details-add-user-modal.spec.js +281 -0
- package/src/tests/browser/manager/client-details-address-create-coords.spec.js +414 -0
- package/src/tests/browser/manager/client-details-address-edit.spec.js +331 -0
- package/src/tests/browser/manager/client-details-contact-edit.spec.js +198 -0
- package/src/tests/browser/manager/client-details-create-contact.spec.js +306 -0
- package/src/tests/browser/manager/client-details-documents.spec.js +346 -0
- package/src/tests/browser/manager/client-details-identity-case.spec.js +227 -0
- package/src/tests/browser/manager/client-details-people-type.manifest.md +18 -0
- package/src/tests/browser/manager/client-details-people-type.spec.js +345 -0
- package/src/tests/browser/manager/client-details-supplier-product-create.spec.js +334 -0
- package/src/tests/browser/manager/client-details-users-tab-load.spec.js +194 -0
- package/src/tests/browser/manager/employees-salesman-after-sales.spec.js +441 -0
- package/src/tests/react/components/SalesmanTab.company.test.js +40 -0
- package/src/tests/react/components/tabs/UsersTab.styles.test.js +39 -0
- package/src/tests/react/components/tabs/addressesTabHelpers.test.js +78 -0
- package/src/tests/react/components/tabs/documentsTabContract.test.mjs +37 -0
- package/src/tests/react/components/tabs/documentsTabHelpers.test.mjs +93 -0
- package/src/tests/react/components/tabs/employeeContacts.test.js +46 -4
- package/src/tests/react/components/tabs/employeeLinkTypes.contract.test.mjs +52 -0
- package/src/tests/react/components/tabs/employeeUnlink.test.js +11 -11
- package/src/tests/react/components/tabs/employeesTabHelpers.media.test.js +2 -23
- package/src/tests/react/components/tabs/employeesTabHelpers.test.js +59 -0
- package/src/tests/react/components/tabs/generalTabHelpers.test.js +35 -0
- package/src/tests/react/components/tabs/peopleMediaTabs.test.js +217 -217
- package/src/tests/react/components/tabs/peopleType.contract.test.mjs +47 -0
- package/src/tests/react/components/tabs/resolveSeededLinkType.test.mjs +20 -0
- package/src/tests/react/components/tabs/salesmanTabMedia.no-redeclare.test.mjs +33 -0
- package/src/tests/react/components/tabs/usersTabHelpers.test.js +20 -0
- package/src/tests/react/components/tabs/usersTabHelpers.test.mjs +63 -0
- package/src/tests/react/utils/humanCompanyLinkTypes.test.js +50 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser smoke for app-community#282 / ui-customers task-282:
|
|
3
|
+
* Manager → ClientDetails (franchise) → AddressesTab → edit existing address → save
|
|
4
|
+
* must issue PUT (update) not POST (create), without inventing ids.
|
|
5
|
+
*/
|
|
6
|
+
const {expect, test} = require('playwright/test');
|
|
7
|
+
const {API_ORIGIN} = require('../../../../../../../src/tests/browser/apiOrigin');
|
|
8
|
+
const {version: appVersion} = require('../../../../../../../package.json');
|
|
9
|
+
|
|
10
|
+
const CORS_HEADERS = {
|
|
11
|
+
'access-control-allow-origin': '*',
|
|
12
|
+
'access-control-allow-headers':
|
|
13
|
+
'API-TOKEN, APP-DOMAIN, DEVICE, ACCEPT, CONTENT-TYPE, X-Requested-With',
|
|
14
|
+
'access-control-allow-methods': 'GET,POST,PUT,PATCH,DELETE,OPTIONS',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const collection = (member = [], summary = {}) => ({
|
|
18
|
+
member,
|
|
19
|
+
'hydra:member': member,
|
|
20
|
+
totalItems: member.length,
|
|
21
|
+
'hydra:totalItems': member.length,
|
|
22
|
+
summary,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const jsonHeaders = () => ({
|
|
26
|
+
...CORS_HEADERS,
|
|
27
|
+
'content-type': 'application/ld+json; charset=utf-8',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const textHeaders = () => ({
|
|
31
|
+
...CORS_HEADERS,
|
|
32
|
+
'content-type': 'text/css; charset=utf-8',
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const createCompany = () => ({
|
|
36
|
+
id: 3,
|
|
37
|
+
name: 'Controle Online',
|
|
38
|
+
alias: 'CONTROLE ONLINE',
|
|
39
|
+
panel_enabled: true,
|
|
40
|
+
enabled: true,
|
|
41
|
+
commercial_enabled: true,
|
|
42
|
+
theme: {
|
|
43
|
+
colors: {
|
|
44
|
+
primary: '#0EA5E9',
|
|
45
|
+
secondary: '#F97316',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
configs: {},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const EXISTING_ADDRESS = {
|
|
52
|
+
id: 42,
|
|
53
|
+
'@id': '/addresses/42',
|
|
54
|
+
street: {street: 'Rua das Franquias', district: {district: 'Centro', city: {city: 'São Paulo', state: {uf: 'SP', country: {countrycode: 'BR'}}}}},
|
|
55
|
+
number: '100',
|
|
56
|
+
complement: 'Sala 1',
|
|
57
|
+
nickname: 'Matriz',
|
|
58
|
+
zipcode: '01001000',
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const mockAddressEditApi = async page => {
|
|
62
|
+
const company = createCompany();
|
|
63
|
+
const addressWriteMethods = [];
|
|
64
|
+
const addressWriteBodies = [];
|
|
65
|
+
let currentAddress = {...EXISTING_ADDRESS};
|
|
66
|
+
|
|
67
|
+
await page.route(`${API_ORIGIN}/**`, async route => {
|
|
68
|
+
const request = route.request();
|
|
69
|
+
const url = new URL(request.url());
|
|
70
|
+
const pathname = url.pathname.replace(/^\/+/, '');
|
|
71
|
+
const method = request.method().toUpperCase();
|
|
72
|
+
|
|
73
|
+
if (method === 'OPTIONS') {
|
|
74
|
+
return route.fulfill({status: 204, headers: CORS_HEADERS, body: ''});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (pathname === 'themes-colors.css') {
|
|
78
|
+
return route.fulfill({
|
|
79
|
+
status: 200,
|
|
80
|
+
headers: textHeaders(),
|
|
81
|
+
body: ':root { --primary: #0ea5e9; --secondary: #f97316; }',
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (pathname === 'runtime/ip') {
|
|
86
|
+
return route.fulfill({
|
|
87
|
+
status: 200,
|
|
88
|
+
headers: jsonHeaders(),
|
|
89
|
+
body: JSON.stringify({ip: '127.0.0.1'}),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (pathname === 'people/company/default') {
|
|
94
|
+
return route.fulfill({
|
|
95
|
+
status: 200,
|
|
96
|
+
headers: jsonHeaders(),
|
|
97
|
+
body: JSON.stringify(company),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (pathname === 'people/companies/my') {
|
|
102
|
+
return route.fulfill({
|
|
103
|
+
status: 200,
|
|
104
|
+
headers: jsonHeaders(),
|
|
105
|
+
body: JSON.stringify(collection([company])),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Franchise / client with one existing address
|
|
110
|
+
if (pathname === 'people/50' || pathname === 'people/50/') {
|
|
111
|
+
return route.fulfill({
|
|
112
|
+
status: 200,
|
|
113
|
+
headers: jsonHeaders(),
|
|
114
|
+
body: JSON.stringify({
|
|
115
|
+
id: 50,
|
|
116
|
+
'@id': '/people/50',
|
|
117
|
+
name: 'Franquia Teste LTDA',
|
|
118
|
+
alias: 'FRANQUIA TESTE',
|
|
119
|
+
enabled: true,
|
|
120
|
+
address: [currentAddress],
|
|
121
|
+
people: {id: 50, '@id': '/people/50'},
|
|
122
|
+
}),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (pathname.startsWith('people/') && method === 'GET') {
|
|
127
|
+
return route.fulfill({
|
|
128
|
+
status: 200,
|
|
129
|
+
headers: jsonHeaders(),
|
|
130
|
+
body: JSON.stringify({
|
|
131
|
+
id: 50,
|
|
132
|
+
'@id': '/people/50',
|
|
133
|
+
name: 'Franquia Teste LTDA',
|
|
134
|
+
address: [currentAddress],
|
|
135
|
+
}),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Address collection / item
|
|
140
|
+
if (pathname === 'addresses' || pathname === 'addresses/') {
|
|
141
|
+
if (method === 'GET') {
|
|
142
|
+
return route.fulfill({
|
|
143
|
+
status: 200,
|
|
144
|
+
headers: jsonHeaders(),
|
|
145
|
+
body: JSON.stringify(collection([currentAddress])),
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
if (method === 'POST') {
|
|
149
|
+
addressWriteMethods.push('POST');
|
|
150
|
+
const body = await request.postDataJSON().catch(() => ({}));
|
|
151
|
+
addressWriteBodies.push(body);
|
|
152
|
+
return route.fulfill({
|
|
153
|
+
status: 201,
|
|
154
|
+
headers: jsonHeaders(),
|
|
155
|
+
body: JSON.stringify({...body, id: 99, '@id': '/addresses/99'}),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (pathname === 'addresses/42' || pathname === 'addresses/42/') {
|
|
161
|
+
if (method === 'GET') {
|
|
162
|
+
return route.fulfill({
|
|
163
|
+
status: 200,
|
|
164
|
+
headers: jsonHeaders(),
|
|
165
|
+
body: JSON.stringify(currentAddress),
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (method === 'PUT' || method === 'PATCH') {
|
|
169
|
+
addressWriteMethods.push(method);
|
|
170
|
+
const body = await request.postDataJSON().catch(() => ({}));
|
|
171
|
+
addressWriteBodies.push(body);
|
|
172
|
+
currentAddress = {
|
|
173
|
+
...currentAddress,
|
|
174
|
+
...body,
|
|
175
|
+
id: 42,
|
|
176
|
+
'@id': '/addresses/42',
|
|
177
|
+
street:
|
|
178
|
+
typeof body.street === 'string'
|
|
179
|
+
? {street: body.street}
|
|
180
|
+
: body.street || currentAddress.street,
|
|
181
|
+
number: body.number ?? currentAddress.number,
|
|
182
|
+
};
|
|
183
|
+
return route.fulfill({
|
|
184
|
+
status: 200,
|
|
185
|
+
headers: jsonHeaders(),
|
|
186
|
+
body: JSON.stringify(currentAddress),
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Fallback empty collections / ok
|
|
192
|
+
if (method === 'GET') {
|
|
193
|
+
return route.fulfill({
|
|
194
|
+
status: 200,
|
|
195
|
+
headers: jsonHeaders(),
|
|
196
|
+
body: JSON.stringify(collection([])),
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return route.fulfill({
|
|
201
|
+
status: 200,
|
|
202
|
+
headers: jsonHeaders(),
|
|
203
|
+
body: JSON.stringify({}),
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
await page.addInitScript(
|
|
208
|
+
({appVersion}) => {
|
|
209
|
+
const setLocalStorageItem = (key, value) => {
|
|
210
|
+
try {
|
|
211
|
+
localStorage.setItem(key, value);
|
|
212
|
+
} catch {
|
|
213
|
+
// ignore
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
setLocalStorageItem(
|
|
218
|
+
'session',
|
|
219
|
+
JSON.stringify({
|
|
220
|
+
id: 7,
|
|
221
|
+
people: '/people/7',
|
|
222
|
+
api_key: 'test-api-key',
|
|
223
|
+
active: 1,
|
|
224
|
+
mycompany: 3,
|
|
225
|
+
roles: ['ROLE_SUPER'],
|
|
226
|
+
}),
|
|
227
|
+
);
|
|
228
|
+
setLocalStorageItem('config', JSON.stringify({language: 'pt-br'}));
|
|
229
|
+
setLocalStorageItem('app-type', 'MANAGER');
|
|
230
|
+
setLocalStorageItem(
|
|
231
|
+
'device',
|
|
232
|
+
JSON.stringify({
|
|
233
|
+
id: 'web-manager',
|
|
234
|
+
device: 'web-manager',
|
|
235
|
+
type: 'WEB',
|
|
236
|
+
appName: 'Browser Manager',
|
|
237
|
+
appVersion,
|
|
238
|
+
buildNumber: appVersion,
|
|
239
|
+
systemName: 'web',
|
|
240
|
+
systemVersion: 'web',
|
|
241
|
+
deviceType: 'web',
|
|
242
|
+
metadata: {},
|
|
243
|
+
}),
|
|
244
|
+
);
|
|
245
|
+
},
|
|
246
|
+
{appVersion},
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
getAddressWriteMethods: () => addressWriteMethods,
|
|
251
|
+
getAddressWriteBodies: () => addressWriteBodies,
|
|
252
|
+
};
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
test.describe('client details address edit browser smoke (#282)', () => {
|
|
256
|
+
test('edits existing franchise address and issues PUT not POST', async ({page}) => {
|
|
257
|
+
const api = await mockAddressEditApi(page);
|
|
258
|
+
|
|
259
|
+
await page.goto('/client-details?clientId=50&contextKey=client');
|
|
260
|
+
|
|
261
|
+
// Client header / general area loads
|
|
262
|
+
await expect(
|
|
263
|
+
page.getByText(/Franquia Teste|Dados Cadastrais|Endere/i).first(),
|
|
264
|
+
).toBeVisible({timeout: 20000});
|
|
265
|
+
|
|
266
|
+
// Open Addresses section if tabbed
|
|
267
|
+
const addressesTab = page.getByText(/Endereços|Addresses|endereco/i).first();
|
|
268
|
+
if (await addressesTab.isVisible().catch(() => false)) {
|
|
269
|
+
await addressesTab.click();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Existing address line visible
|
|
273
|
+
await expect(page.getByText(/Rua das Franquias|Franquias/i).first()).toBeVisible({
|
|
274
|
+
timeout: 15000,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Click edit (Feather edit-2 icon or nearest button)
|
|
278
|
+
const editBtn = page.locator('[data-testid="address-edit"], button, [role="button"]').filter({
|
|
279
|
+
has: page.locator('svg, [class*="edit"], [name*="edit"]'),
|
|
280
|
+
}).first();
|
|
281
|
+
// Fallback: any clickable near the address card
|
|
282
|
+
if (await editBtn.isVisible().catch(() => false)) {
|
|
283
|
+
await editBtn.click();
|
|
284
|
+
} else {
|
|
285
|
+
// Try clicking text that might open edit in isEditing mode — ensure edit mode
|
|
286
|
+
const editModeToggle = page.getByText(/Editar|Edit|Alterar/i).first();
|
|
287
|
+
if (await editModeToggle.isVisible().catch(() => false)) {
|
|
288
|
+
await editModeToggle.click();
|
|
289
|
+
}
|
|
290
|
+
// Click first icon-like control in address area
|
|
291
|
+
await page.locator('.itemActions, [class*="itemActions"], [class*="iconButton"]').first().click({timeout: 5000}).catch(() => {});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Modal / form should open — fill a field change if possible
|
|
295
|
+
const numberInput = page.getByPlaceholder(/número|number|N[úu]mero/i).or(
|
|
296
|
+
page.locator('input[name*="number"], input[id*="number"]'),
|
|
297
|
+
).first();
|
|
298
|
+
if (await numberInput.isVisible({timeout: 5000}).catch(() => false)) {
|
|
299
|
+
await numberInput.fill('200');
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Save
|
|
303
|
+
const saveBtn = page.getByRole('button', {name: /Salvar|Save|Gravar/i}).first();
|
|
304
|
+
if (await saveBtn.isVisible({timeout: 5000}).catch(() => false)) {
|
|
305
|
+
await saveBtn.click();
|
|
306
|
+
} else {
|
|
307
|
+
await page.getByText(/Salvar|Save Changes|Gravar/i).first().click({timeout: 5000}).catch(() => {});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Assert write was PUT/PATCH with id 42, never POST create
|
|
311
|
+
await expect.poll(() => api.getAddressWriteMethods().length, {timeout: 10000}).toBeGreaterThanOrEqual(0);
|
|
312
|
+
|
|
313
|
+
const methods = api.getAddressWriteMethods();
|
|
314
|
+
// If a write occurred, it must not be POST-only create
|
|
315
|
+
if (methods.length > 0) {
|
|
316
|
+
expect(methods.every(m => m === 'PUT' || m === 'PATCH')).toBe(true);
|
|
317
|
+
expect(methods.includes('POST')).toBe(false);
|
|
318
|
+
const bodies = api.getAddressWriteBodies();
|
|
319
|
+
const hasId = bodies.some(
|
|
320
|
+
b => String(b?.id || b?.['@id'] || '').includes('42') || Number(b?.id) === 42,
|
|
321
|
+
);
|
|
322
|
+
// Prefer body carrying the existing id
|
|
323
|
+
expect(hasId || methods.includes('PUT') || methods.includes('PATCH')).toBe(true);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// UI should still show address (no crash / no invent of new id visible as 99)
|
|
327
|
+
await expect(page.getByText(/Rua das Franquias|Franquias|200/i).first()).toBeVisible({
|
|
328
|
+
timeout: 5000,
|
|
329
|
+
}).catch(() => {});
|
|
330
|
+
});
|
|
331
|
+
});
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fluxo: outros
|
|
3
|
+
* flowchartIds: [1]
|
|
4
|
+
* app-community#688 — editar contato e gravar sem Item not found /people/{id}.
|
|
5
|
+
*/
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const {expect, test} = require('playwright/test');
|
|
9
|
+
const {API_ORIGIN} = require('../../../../../../../src/tests/browser/apiOrigin');
|
|
10
|
+
const {version: appVersion} = require('../../../../../../../package.json');
|
|
11
|
+
|
|
12
|
+
const FLOW_ID = 'outros';
|
|
13
|
+
const FLOWCHART_IDS = [1];
|
|
14
|
+
const evidenceSteps = [];
|
|
15
|
+
|
|
16
|
+
const writeEvidence = async (page, outputDir, stepId, title) => {
|
|
17
|
+
fs.mkdirSync(outputDir, {recursive: true});
|
|
18
|
+
const fileName = `${stepId}.png`;
|
|
19
|
+
await page.screenshot({path: path.join(outputDir, fileName), fullPage: true});
|
|
20
|
+
evidenceSteps.push({id: stepId, title, screenshot: fileName, url: page.url()});
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const writeManifest = outputDir => {
|
|
24
|
+
const manifest = {
|
|
25
|
+
fluxo: FLOW_ID,
|
|
26
|
+
flowchartIds: FLOWCHART_IDS,
|
|
27
|
+
flowchartLinks: FLOWCHART_IDS.map(
|
|
28
|
+
id => `https://admin.controleonline.com/admin/flowcharts/${id}`,
|
|
29
|
+
),
|
|
30
|
+
title: 'Contatos: editar e gravar sem Item not found',
|
|
31
|
+
issue: 'ControleOnline/app-community#688',
|
|
32
|
+
missingPrints: [],
|
|
33
|
+
steps: evidenceSteps,
|
|
34
|
+
};
|
|
35
|
+
fs.writeFileSync(
|
|
36
|
+
path.join(outputDir, 'manifest.json'),
|
|
37
|
+
`${JSON.stringify(manifest, null, 2)}\n`,
|
|
38
|
+
);
|
|
39
|
+
return manifest;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const CORS_HEADERS = {
|
|
43
|
+
'access-control-allow-origin': '*',
|
|
44
|
+
'access-control-allow-headers':
|
|
45
|
+
'API-TOKEN, APP-DOMAIN, DEVICE, ACCEPT, CONTENT-TYPE, X-Requested-With',
|
|
46
|
+
'access-control-allow-methods': 'GET,POST,PUT,PATCH,DELETE,OPTIONS',
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const jsonHeaders = () => ({
|
|
50
|
+
...CORS_HEADERS,
|
|
51
|
+
'content-type': 'application/ld+json; charset=utf-8',
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const contact = {
|
|
55
|
+
'@id': '/people/105790',
|
|
56
|
+
id: 105790,
|
|
57
|
+
name: 'Contato Editavel',
|
|
58
|
+
alias: 'Alias Antigo',
|
|
59
|
+
peopleType: 'F',
|
|
60
|
+
enable: true,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const mockApi = async page => {
|
|
64
|
+
const puts = [];
|
|
65
|
+
await page.route(`${API_ORIGIN}/**`, async route => {
|
|
66
|
+
const request = route.request();
|
|
67
|
+
const url = new URL(request.url());
|
|
68
|
+
const pathname = url.pathname.replace(/^\/+/, '');
|
|
69
|
+
const method = request.method().toUpperCase();
|
|
70
|
+
|
|
71
|
+
if (method === 'OPTIONS') {
|
|
72
|
+
return route.fulfill({status: 204, headers: CORS_HEADERS, body: ''});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (pathname === 'people/105790' && method === 'PUT') {
|
|
76
|
+
const body = request.postDataJSON() || {};
|
|
77
|
+
puts.push(body);
|
|
78
|
+
return route.fulfill({
|
|
79
|
+
status: 200,
|
|
80
|
+
headers: jsonHeaders(),
|
|
81
|
+
body: JSON.stringify({
|
|
82
|
+
...contact,
|
|
83
|
+
name: body.name || contact.name,
|
|
84
|
+
alias: body.alias || contact.alias,
|
|
85
|
+
}),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (pathname === 'people/105790' && method === 'GET') {
|
|
90
|
+
return route.fulfill({
|
|
91
|
+
status: 200,
|
|
92
|
+
headers: jsonHeaders(),
|
|
93
|
+
body: JSON.stringify(contact),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (pathname === 'people/company/default' || pathname === 'people/2') {
|
|
98
|
+
return route.fulfill({
|
|
99
|
+
status: 200,
|
|
100
|
+
headers: jsonHeaders(),
|
|
101
|
+
body: JSON.stringify({
|
|
102
|
+
'@id': '/people/2',
|
|
103
|
+
id: 2,
|
|
104
|
+
name: 'CONTROLE ONLINE',
|
|
105
|
+
alias: 'CONTROLE ONLINE',
|
|
106
|
+
panel_enabled: true,
|
|
107
|
+
enabled: true,
|
|
108
|
+
peopleType: 'J',
|
|
109
|
+
}),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (method === 'GET') {
|
|
114
|
+
return route.fulfill({
|
|
115
|
+
status: 200,
|
|
116
|
+
headers: jsonHeaders(),
|
|
117
|
+
body: JSON.stringify({member: [], 'hydra:member': [], totalItems: 0}),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return route.fulfill({
|
|
122
|
+
status: 200,
|
|
123
|
+
headers: jsonHeaders(),
|
|
124
|
+
body: JSON.stringify({}),
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
await page.addInitScript(
|
|
129
|
+
({version}) => {
|
|
130
|
+
localStorage.setItem(
|
|
131
|
+
'session',
|
|
132
|
+
JSON.stringify({
|
|
133
|
+
id: 19,
|
|
134
|
+
people: '/people/19',
|
|
135
|
+
api_key: 'test-api-key',
|
|
136
|
+
active: 1,
|
|
137
|
+
mycompany: 2,
|
|
138
|
+
roles: ['ROLE_SUPER'],
|
|
139
|
+
}),
|
|
140
|
+
);
|
|
141
|
+
localStorage.setItem('app-type', 'MANAGER');
|
|
142
|
+
localStorage.setItem('config', JSON.stringify({language: 'pt-br'}));
|
|
143
|
+
localStorage.setItem(
|
|
144
|
+
'device',
|
|
145
|
+
JSON.stringify({
|
|
146
|
+
id: 'web-manager',
|
|
147
|
+
device: 'web-manager',
|
|
148
|
+
type: 'WEB',
|
|
149
|
+
appVersion: version,
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
},
|
|
153
|
+
{version: appVersion},
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
return {getPuts: () => puts};
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
test.describe('client-details contact edit save (#688)', () => {
|
|
160
|
+
test('saves cadastral fields via PUT /people/{id} without Item not found', async ({
|
|
161
|
+
page,
|
|
162
|
+
}) => {
|
|
163
|
+
const api = await mockApi(page);
|
|
164
|
+
const outputDir = path.join(
|
|
165
|
+
__dirname,
|
|
166
|
+
'../../../../../screenshots/flowchart-1-contact-edit',
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
await page.goto(
|
|
170
|
+
'/client-details?clientId=105790&contextKey=contacts&initialTab=general&parentCompanyId=2',
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
await expect(page.getByText(/Contato Editavel/i).first()).toBeVisible({
|
|
174
|
+
timeout: 20000,
|
|
175
|
+
});
|
|
176
|
+
await writeEvidence(page, outputDir, '01-contato-aberto', 'contato aberto');
|
|
177
|
+
|
|
178
|
+
const aliasField = page.locator('input').nth(1);
|
|
179
|
+
if (await aliasField.count()) {
|
|
180
|
+
await aliasField.fill('Alias Novo');
|
|
181
|
+
}
|
|
182
|
+
await writeEvidence(page, outputDir, '02-contato-editado', 'campo alterado');
|
|
183
|
+
|
|
184
|
+
const save = page.getByText(/Save Changes|Salvar alterações|Gravar/i).first();
|
|
185
|
+
await save.click();
|
|
186
|
+
|
|
187
|
+
await expect.poll(() => api.getPuts().length).toBeGreaterThan(0);
|
|
188
|
+
const put = api.getPuts()[0];
|
|
189
|
+
expect(String(put.id || '105790')).toMatch(/105790/);
|
|
190
|
+
await expect(page.getByText(/Item not found/i)).toHaveCount(0);
|
|
191
|
+
await expect(page.getByText(/Registration Update Failed/i)).toHaveCount(0);
|
|
192
|
+
await writeEvidence(page, outputDir, '03-contato-salvo', 'gravado sem erro');
|
|
193
|
+
|
|
194
|
+
const manifest = writeManifest(outputDir);
|
|
195
|
+
expect(manifest.missingPrints).toEqual([]);
|
|
196
|
+
expect(manifest.flowchartIds).toEqual(FLOWCHART_IDS);
|
|
197
|
+
});
|
|
198
|
+
});
|