Skip to content

Commit 7a82d4c

Browse files
authored
Merge pull request #119 from eviltester/117-ui-bug-fixes-and-alert-usage
117 UI bug fixes and alert usage
2 parents fedfa19 + ae97c3c commit 7a82d4c

51 files changed

Lines changed: 3009 additions & 712 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/cli/src/run-cli.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,20 @@ function writeLine(output, text = '') {
1010
output(`${text}\n`);
1111
}
1212

13+
function formatCanonicalErrors(errors = []) {
14+
const safeErrors = Array.isArray(errors) ? errors : [];
15+
return safeErrors.map((error) => {
16+
if (!error || typeof error !== 'object') {
17+
return String(error ?? '');
18+
}
19+
const code = String(error.code || 'error');
20+
const message = String(error.message || '');
21+
const column = error.column != null ? ` column=${error.column}` : '';
22+
const line = Number.isInteger(error.line) ? ` line=${error.line}` : '';
23+
return `[${code}] ${message}${column}${line}`.trim();
24+
});
25+
}
26+
1327
export async function runCliCommand({ options, platform }) {
1428
const progress = (message) => {
1529
if (options.showProgress) {
@@ -78,7 +92,7 @@ export async function runCliCommand({ options, platform }) {
7892
});
7993

8094
if (!result.ok) {
81-
writeLine(platform.stderr, result.errors.join('\n'));
95+
writeLine(platform.stderr, formatCanonicalErrors(result.errors).join('\n'));
8296
return 1;
8397
}
8498

@@ -128,7 +142,7 @@ export async function runCliCommand({ options, platform }) {
128142
});
129143

130144
if (!streamResult.ok) {
131-
writeLine(platform.stderr, streamResult.errors.join('\n'));
145+
writeLine(platform.stderr, formatCanonicalErrors(streamResult.errors).join('\n'));
132146
return 1;
133147
}
134148

@@ -176,7 +190,7 @@ export async function runCliCommand({ options, platform }) {
176190
});
177191

178192
if (!result.ok) {
179-
writeLine(platform.stderr, result.errors.join('\n'));
193+
writeLine(platform.stderr, formatCanonicalErrors(result.errors).join('\n'));
180194
return 1;
181195
}
182196

apps/web/src/tests/browser/abstraction-smoke-tests/grid-header-controls.spec.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,26 @@ test('grid header sorting and per-column filter change visible results', async (
6060
.toEqual(['Apple', 'Banana', 'Cherry']);
6161
expect(pageErrors).toEqual([]);
6262
});
63+
64+
test('grid header validation errors render in inline grid error surface', async ({ page }) => {
65+
const pageErrors = trackPageErrors(page);
66+
const appPage = new AppPage(page);
67+
68+
await appPage.goto();
69+
await appPage.gridEditor.setUniqueColumnNames(true);
70+
const initialColumnName = (await appPage.gridEditor.header.getColumnNames())[0];
71+
const gridError = page.locator('#grid-column-error');
72+
73+
await appPage.gridEditor.header.addColumnRight(initialColumnName, 'Existing Name');
74+
await expect.poll(async () => appPage.gridEditor.header.getColumnNames()).toContain('Existing Name');
75+
76+
await appPage.gridEditor.header.renameColumn(initialColumnName, 'Existing Name');
77+
await expect(gridError).toContainText('A column with name Existing Name already exists');
78+
79+
await appPage.gridEditor.header.deleteColumn('Existing Name');
80+
await expect.poll(async () => appPage.gridEditor.header.getColumnNames()).toEqual([initialColumnName]);
81+
await appPage.gridEditor.header.deleteColumn(initialColumnName);
82+
await expect(gridError).toContainText('Cannot Delete The Only Column');
83+
84+
expect(pageErrors).toEqual([]);
85+
});

apps/web/src/tests/browser/abstractions/components/grid-editor.component.js

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class GridEditorComponent {
1717
this.clearFiltersButton = page.getByRole('button', { name: 'Clear Filters' });
1818
this.clearSortButton = page.getByRole('button', { name: 'Clear Sort' });
1919
this.resetTableButton = page.getByRole('button', { name: 'Reset Table' });
20+
this.uniqueColumnNamesCheckbox = page.locator('#uniqueColumnNamesCheckbox');
2021
}
2122

2223
async expectVisible() {
@@ -64,17 +65,8 @@ class GridEditorComponent {
6465
}
6566

6667
async deleteSelectedRows() {
67-
const handler = async (dialog) => {
68-
if (dialog.message() === 'Are you Sure You Want to Delete Rows?') {
69-
await dialog.accept();
70-
}
71-
};
72-
this.page.on('dialog', handler);
73-
try {
74-
await this.deleteSelectedRowsButton.click();
75-
} finally {
76-
this.page.off('dialog', handler);
77-
}
68+
await this.deleteSelectedRowsButton.click();
69+
await this._acceptConfirmIfVisible();
7870
}
7971

8072
async selectRow(rowIndex) {
@@ -179,10 +171,24 @@ class GridEditorComponent {
179171
}
180172

181173
async resetTable() {
182-
this.page.once('dialog', async (dialog) => {
183-
await dialog.accept();
184-
});
185174
await this.resetTableButton.click();
175+
await this._acceptConfirmIfVisible();
176+
}
177+
178+
async setUniqueColumnNames(enabled = true) {
179+
if (enabled) {
180+
await this.uniqueColumnNamesCheckbox.check();
181+
return;
182+
}
183+
await this.uniqueColumnNamesCheckbox.uncheck();
184+
}
185+
186+
async _acceptConfirmIfVisible() {
187+
const confirmBackdrop = this.page.locator('#confirm-modal-backdrop');
188+
if ((await confirmBackdrop.count()) > 0 && (await confirmBackdrop.isVisible())) {
189+
await confirmBackdrop.locator('#confirm-modal-ok').click();
190+
await expect(confirmBackdrop).toBeHidden();
191+
}
186192
}
187193
}
188194

apps/web/src/tests/browser/abstractions/components/grid-header.component.js

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,38 +46,32 @@ class GridHeaderComponent {
4646
}
4747

4848
async renameColumn(columnName, newName) {
49-
this.page.once('dialog', async (dialog) => {
50-
await dialog.accept(newName);
51-
});
5249
await this.clickAction(columnName, 'rename');
50+
await this.fillTextInputModal(newName);
5351
}
5452

5553
async addColumnLeft(columnName, newName) {
56-
this.page.once('dialog', async (dialog) => {
57-
await dialog.accept(newName);
58-
});
5954
await this.clickAction(columnName, 'add-left');
55+
await this.fillTextInputModal(newName);
6056
}
6157

6258
async addColumnRight(columnName, newName) {
63-
this.page.once('dialog', async (dialog) => {
64-
await dialog.accept(newName);
65-
});
6659
await this.clickAction(columnName, 'add-right');
60+
await this.fillTextInputModal(newName);
6761
}
6862

6963
async duplicateColumn(columnName, newName) {
70-
this.page.once('dialog', async (dialog) => {
71-
await dialog.accept(newName);
72-
});
7364
await this.clickAction(columnName, 'duplicate');
65+
await this.fillTextInputModal(newName);
7466
}
7567

7668
async deleteColumn(columnName) {
77-
this.page.once('dialog', async (dialog) => {
78-
await dialog.accept();
79-
});
8069
await this.clickAction(columnName, 'delete');
70+
const confirmBackdrop = this.page.locator('#confirm-modal-backdrop');
71+
if ((await confirmBackdrop.count()) > 0 && (await confirmBackdrop.isVisible())) {
72+
await confirmBackdrop.locator('#confirm-modal-ok').click();
73+
await expect(confirmBackdrop).toBeHidden();
74+
}
8175
}
8276

8377
async sortAsc(columnName) {
@@ -131,6 +125,15 @@ class GridHeaderComponent {
131125
.split('\n')[0]
132126
.trim();
133127
}
128+
129+
async fillTextInputModal(value) {
130+
const backdrop = this.page.locator('#text-input-modal-backdrop');
131+
await expect(backdrop).toBeVisible();
132+
const input = backdrop.locator('#text-input-modal-field');
133+
await input.fill(String(value ?? ''));
134+
await backdrop.locator('#text-input-modal-ok').click();
135+
await expect(backdrop).toBeHidden();
136+
}
134137
}
135138

136139
module.exports = { GridHeaderComponent };

apps/web/src/tests/browser/abstractions/components/tabbed-text.component.js

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,23 +58,13 @@ class TabbedTextComponent {
5858
}
5959

6060
async preview() {
61-
this.page.once('dialog', async (dialog) => {
62-
await dialog.accept();
63-
});
6461
await this.previewOrEditButton.click();
62+
await this._resolveConfirmModal(true);
6563
}
6664

6765
async togglePreviewEdit(confirmPrompt = true) {
68-
if (confirmPrompt === true) {
69-
this.page.once('dialog', async (dialog) => {
70-
await dialog.accept();
71-
});
72-
} else if (confirmPrompt === false) {
73-
this.page.once('dialog', async (dialog) => {
74-
await dialog.dismiss();
75-
});
76-
}
7766
await this.previewOrEditButton.click();
67+
await this._resolveConfirmModal(confirmPrompt !== false);
7868
}
7969

8070
async isCopyButtonVisible() {
@@ -129,6 +119,19 @@ class TabbedTextComponent {
129119
async expectOutputContains(value) {
130120
await expect(this.outputTextArea).toHaveValue(new RegExp(value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
131121
}
122+
123+
async _resolveConfirmModal(accept = true) {
124+
const confirmBackdrop = this.page.locator('#confirm-modal-backdrop');
125+
if ((await confirmBackdrop.count()) === 0 || !(await confirmBackdrop.isVisible())) {
126+
return;
127+
}
128+
if (accept) {
129+
await confirmBackdrop.locator('#confirm-modal-ok').click();
130+
} else {
131+
await confirmBackdrop.locator('#confirm-modal-cancel').click();
132+
}
133+
await expect(confirmBackdrop).toBeHidden();
134+
}
132135
}
133136

134137
module.exports = { TabbedTextComponent };

0 commit comments

Comments
 (0)