Skip to content

Commit 7d0a4b0

Browse files
authored
fix: restore getter error details (#631)
* fix: restore getter error details (#596) broke QUnit error messages so they now miss contextual error info like page object path and selector. This happened cause #596 relied on the `.toString()` method of the Error to build the final error message. However, that's not how QUnit is displaying the error message. It just uses the `Error.message`. So let's build the final message in the `Error` constructor.
1 parent 4a93091 commit 7d0a4b0

File tree

5 files changed

+69
-31
lines changed

5 files changed

+69
-31
lines changed

addon/src/-private/better-errors.js

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,47 +22,63 @@ export function throwContextualError(node, filters, e) {
2222
export function throwBetterError(node, key, error, { selector } = {}) {
2323
let message = error instanceof Error ? error.message : error.toString();
2424

25-
const err = new PageObjectError(key, node, selector, message);
25+
const wrapperError = new PageObjectError(message, {
26+
cause: {
27+
message,
28+
key,
29+
node,
30+
selector,
31+
},
32+
});
33+
2634
if (error instanceof Error && 'stack' in error) {
27-
err.stack = error.stack;
35+
wrapperError.stack = error.stack;
2836
}
2937

30-
console.error(err.toString());
31-
throw err;
38+
console.error(wrapperError.toString());
39+
throw wrapperError;
3240
}
3341

3442
export class PageObjectError extends Error {
35-
constructor(label, node, selector, ...args) {
36-
super(...args);
43+
constructor(message, options = {}, ...args) {
44+
const { cause } = options;
45+
const { node, key, selector } = cause || {};
46+
47+
const errorDescription = buildErrorDescription(node, key, selector);
3748

38-
this.label = label;
39-
this.node = node;
40-
this.selector = selector;
49+
super(
50+
[message, errorDescription].filter(Boolean).join('\n'),
51+
options,
52+
...args
53+
);
4154
}
55+
}
4256

43-
toString() {
44-
let { message, label, node, selector } = this;
45-
if (label) {
46-
let path = buildPropertyNamesPath(label, node);
47-
message = `${message}\n\nPageObject: '${path.join('.')}'`;
48-
}
57+
function buildErrorDescription(node, key, selector) {
58+
const lines = [];
4959

50-
if (typeof selector === 'string' && selector.trim().length > 0) {
51-
message = `${message}\n Selector: '${selector}'`;
52-
}
60+
const path = buildPropertyNamesPath(node);
61+
if (key) {
62+
path.push(key);
63+
}
64+
lines.push(`\nPageObject: '${path.join('.')}'`);
5365

54-
return `Error: ${message}`;
66+
if (typeof selector === 'string' && selector.trim().length > 0) {
67+
lines.push(` Selector: '${selector}'`);
5568
}
69+
70+
return lines.join('\n');
5671
}
5772

58-
function buildPropertyNamesPath(leafKey, node) {
59-
let path = [leafKey];
73+
function buildPropertyNamesPath(node) {
74+
let path = [];
6075

6176
let current;
6277
for (current = node; current; current = Ceibo.parent(current)) {
6378
path.unshift(Ceibo.meta(current).key);
6479
}
6580

81+
// replace "root" with "page"
6682
path[0] = 'page';
6783

6884
return path;

addon/src/macros/getter.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,17 @@ export function getter(fn) {
4949
return fn.call(this, pageObjectKey);
5050
} catch (e) {
5151
if (e instanceof PageObjectError) {
52-
if (!e.label) {
53-
e.label = pageObjectKey;
52+
if (!e.cause.key) {
53+
// re-throw with a `pageObjectKey` to have a complete error message
54+
const wrapperError = new PageObjectError(e.cause.message, {
55+
cause: {
56+
...e.cause,
57+
key: pageObjectKey,
58+
},
59+
});
60+
wrapperError.stack = e.stack;
61+
62+
throw wrapperError;
5463
}
5564

5665
throw e;

test-app/tests/integration/comma-separated-selector-test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ module('comma separated selectors', function (hooks) {
1616

1717
assert.throws(
1818
() => page.isVisible,
19-
new Error(
19+
new RegExp(
2020
'Usage of comma separated selectors is not supported. Please make sure your selector targets a single selector.'
2121
)
2222
);
@@ -31,7 +31,7 @@ module('comma separated selectors', function (hooks) {
3131

3232
assert.throws(
3333
() => page.text,
34-
new Error(
34+
new RegExp(
3535
'Usage of comma separated selectors is not supported. Please make sure your selector targets a single selector.'
3636
)
3737
);
@@ -50,7 +50,7 @@ module('comma separated selectors', function (hooks) {
5050

5151
assert.throws(
5252
() => page.text,
53-
new Error(
53+
new RegExp(
5454
'Usage of comma separated selectors is not supported. Please make sure your selector targets a single selector.'
5555
)
5656
);

test-app/tests/unit/-private/properties/getter-test.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ module('getter', function (hooks) {
8989

9090
try {
9191
page.foo;
92-
assert.true(false);
92+
assert.false(true, 'should not succeed');
9393
} catch (e) {
9494
assert.strictEqual(
9595
e?.toString(),
@@ -109,6 +109,14 @@ PageObject: 'page.foo'`
109109
}),
110110
});
111111

112-
assert.throws(() => page.foo, /Selector: '.non-existing-scope'/);
112+
try {
113+
page.foo;
114+
assert.false(true, 'should not succeed');
115+
} catch (e: any) {
116+
assert.strictEqual(
117+
e.toString(),
118+
`Error: Element not found.\n\nPageObject: 'page.foo'\n Selector: '.non-existing-scope'`
119+
);
120+
}
113121
});
114122
});

test-app/tests/unit/extend/find-one-test.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,18 @@ module(`Extend | findOne`, function (hooks) {
4040
});
4141

4242
test('throws error if 0 elements found', async function (assert) {
43-
const page = create({});
43+
const page = create({
44+
child: {},
45+
});
4446

4547
await render(hbs`<span class="ipsum"></span>`);
4648

4749
assert.throws(
48-
() => findOne(page, '.unknown', {}),
49-
/Error: Element not found./
50+
() => findOne(page.child, '.unknown', {}),
51+
new Error(`Element not found.
52+
53+
PageObject: 'page.child'
54+
Selector: '.unknown'`)
5055
);
5156
});
5257

0 commit comments

Comments
 (0)