Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(engine): clean up error messages #1193

Merged
merged 24 commits into from
May 23, 2019
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
697cc77
refactor(engine): tweak error messages for attributes.ts
ekashida Apr 23, 2019
01689a6
refactor(engine): tweak error messages for restrictions.ts
ekashida Apr 23, 2019
37a32f6
chore: review feedback
ekashida Apr 24, 2019
89234d8
refactor: remove incorrect error
ekashida Apr 24, 2019
efef8d5
refactor: remove error and add reflective to global style attr
ekashida Apr 24, 2019
f677bd9
chore: tweaks
ekashida Apr 28, 2019
e81a86d
refactor: remove error logs for experimental properties
ekashida Apr 29, 2019
b4d3411
chore: remove readonly error in getter and update wording
ekashida Apr 30, 2019
b2146b0
chore: check that the global attr matches the attr being set
ekashida Apr 30, 2019
c749398
chore: review feedback
ekashida Apr 30, 2019
97e0da9
chore: review feedback
ekashida May 1, 2019
a8e0664
chore: redefine getGlobalHTMLPropertiesInfo as an object
ekashida May 1, 2019
9f83c19
refactor: remove logWarning
ekashida May 1, 2019
598c7b4
chore: adjustments
ekashida May 1, 2019
db4983a
refactor: relax url validation for href attribute
ekashida May 1, 2019
764fd61
chore: remove errors
ekashida May 23, 2019
8fdfcc2
chore: convert straggling logWarning to logError
ekashida May 23, 2019
de16257
chore: remove logged error from base-bridge-element.ts
ekashida May 23, 2019
9cae359
chore: remove logged error from base-bridge-element.ts
ekashida May 23, 2019
042f838
chore: remove logged error from template.ts
ekashida May 23, 2019
e21fd2d
chore: remove logged error from restrictions.ts
ekashida May 23, 2019
7ff440a
test: update tests to reflect error message changes
ekashida May 23, 2019
5b581fb
chore: add back validation for unknown props in the template
ekashida May 23, 2019
248e74a
fix: ignore default slot content when validating slot names
ekashida May 23, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,4 @@ function createMatcher(methodName) {

module.exports = {
toLogError: createMatcher('logError'),
toLogWarning: createMatcher('logWarning'),
};
26 changes: 10 additions & 16 deletions packages/@lwc/engine/scripts/jest/setup-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ const chalk = require('chalk');
require('@lwc/synthetic-shadow');

const { CONSOLE_WHITELIST } = require('./test-whitelist');
const { toLogError, toLogWarning } = require('./matchers/log-matchers');
const { toLogError } = require('./matchers/log-error');

// Extract original methods from console
const { warn: originalWarn, error: originalError } = console;
const { error: originalError } = console;

let currentSpec;
jasmine.getEnv().addReporter({
Expand All @@ -28,50 +28,44 @@ jasmine.getEnv().addReporter({
});

beforeEach(() => {
console.warn = jest.spyOn(console, 'warn');
console.error = jest.spyOn(console, 'error');
});

afterEach(() => {
const { fullName } = currentSpec;

const isWhitelistedTest = CONSOLE_WHITELIST.includes(fullName);
const didTestLogged = [...console.warn.mock.calls, ...console.error.mock.calls].length > 0;
const didTestLogged = console.error.mock.calls.length > 0;

try {
if (isWhitelistedTest) {
if (!didTestLogged) {
const boldedName = chalk.green.bold(fullName);
const boldedFile = chalk.green.bold('test-whitelist.js');
const message = [
`This test used to used to log a warning or an error, but don't log anymore.`,
`Please remove "${chalk.green.bold(fullName)}" from "${chalk.green.bold(
'test-whitelist.js'
)}"`,
`This test used to log an error but no longer does.`,
`Please remove "${boldedName}" from "${boldedFile}"`,
].join('\n');

throw new Error(message);
}
} else {
if (didTestLogged) {
const message = [
`Expect test not to log an error or a warning.\n`,
`If the message expected, make sure you asserts against those logs in the tests.\n`,
`Use instead: ${chalk.green.bold(
`expect(<function>).toLogError(<message>)`
)} or ${chalk.green.bold(`expect(<function>).toLogWarning(<message>)`)}`,
`Expected the test to not result in an error being logged.`,
`If this is expected, make sure to add an assertion for it:`,
`${chalk.green.bold(`expect(<function>).toLogError(<message>)`)}`,
].join('\n');

throw new Error(message);
}
}
} finally {
// Make sure to reset the original console methods after each tests
console.warn = originalWarn;
console.error = originalError;
}
});

// Register custom console matchers in jasmine
expect.extend({
toLogError,
toLogWarning,
});
1 change: 0 additions & 1 deletion packages/@lwc/engine/scripts/jest/test-whitelist.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ const CONSOLE_WHITELIST = [
'html-element global HTML Properties should log console error when user land code changes attribute via querySelector',
'html-element global HTML Properties should log console error when user land code removes attribute via querySelector',
'html-element global HTML Properties should log error message when attribute is set via elm.setAttribute if reflective property is defined',
'html-element life-cycles should not throw error when accessing a non-observable property from tracked property when not rendering',
];

for (let i = 0; i < CONSOLE_WHITELIST.length; i++) {
Expand Down
6 changes: 3 additions & 3 deletions packages/@lwc/engine/src/framework/__tests__/api.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ describe('api', () => {
const elm = createElement('x-vm-aux', { is: VmRendering });
expect(() => {
document.body.appendChild(elm);
}).toLogWarning('it should be an Array or an iterable Object.');
}).toLogError('It must be an Array or an iterable Object.');
});
});

Expand Down Expand Up @@ -225,7 +225,7 @@ describe('api', () => {
const elm = createElement('x-foo', { is: Foo });
expect(() => {
document.body.appendChild(elm);
}).toLogWarning('This attribute can only be set to 0 or -1.');
}).toLogError('This attribute must be set to 0 or -1.');
expect(normalized).toBe(0);
});

Expand Down Expand Up @@ -275,7 +275,7 @@ describe('api', () => {
const elm = createElement('x-foo', { is: Foo });
expect(() => {
document.body.appendChild(elm);
}).toLogWarning('This attribute can only be set to 0 or -1.');
}).toLogError('This attribute must be set to 0 or -1.');
expect(normalized).toBe(0);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ describe('html-element', () => {
MyComponent.track = { state: 1 };
const elm = createElement('x-foo-tracked-null', { is: MyComponent });

expect(() => document.body.appendChild(elm)).not.toLogWarning();
expect(() => document.body.appendChild(elm)).not.toLogError();
});

it('should not log a warning when initializing api value to null', function() {
Expand All @@ -518,7 +518,7 @@ describe('html-element', () => {
};
const elm = createElement('x-foo-init-api', { is: MyComponent });

expect(() => document.body.appendChild(elm)).not.toLogWarning();
expect(() => document.body.appendChild(elm)).not.toLogError();
});
});

Expand Down Expand Up @@ -1386,7 +1386,7 @@ describe('html-element', () => {
expect(() => {
createElement('prop-setter-title', { is: MyComponent });
}).toLogError(
`constructor should not read the value of property "title". The owner component has not yet set the value. Instead use the constructor to set default values for properties.`
"`PatchedHTMLElement` constructor can't read the value of property `title` because the owner component hasn't set the value yet. Instead, use the `PatchedHTMLElement` constructor to set a default value for the property."
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think of 'parent' instead of 'owner'?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the second note, should we point the user to the lifecycle hook during the invocation of which the owner has already set the values? Such as connected callback?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@apapko I think suggesting a different lifecycle hook might be assuming too much since we don't know what the developer is trying to do. They might have some logic which doesn't care about whether the value is the initialized value or the owner-provided value.

The error message does seem a little weird though since it looks like it'll get logged even for

class Foo extends LightningElement {
  @api
  coffee = 'black';

  constructor() {
      super();
      this._coffee = this.coffee;
  }
}

@caridy Seems like another candidate for removal?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For posterity, we discussed this and agreed it should be removed since we can't predict the author's intention.

);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ describe('scoped-ids', () => {
const elm = createElement('x-foo', { is: MyComponent });
expect(() => {
document.body.appendChild(elm);
}).toLogError('Invalid id value "undefined". Expected a non-empty string.');
}).toLogError(
'Invalid id value "undefined". The id attribute must contain a non-empty string.'
);
const div = elm.shadowRoot.querySelector('div');
expect(div.getAttribute('id')).toEqual(null);
});
Expand All @@ -104,7 +106,9 @@ describe('scoped-ids', () => {
const elm = createElement('x-foo', { is: MyComponent });
expect(() => {
document.body.appendChild(elm);
}).toLogError('Invalid id value "". Expected a non-empty string.');
}).toLogError(
'Invalid id value "". The id attribute must contain a non-empty string.'
);
const div = elm.shadowRoot.querySelector('div');
expect(div.getAttribute('id')).toEqual('');
});
Expand Down
36 changes: 19 additions & 17 deletions packages/@lwc/engine/src/framework/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,8 @@ export function h(sel: string, data: ElementCompilerData, children: VNodes): VEl
`vnode.data.styleMap and vnode.data.style ambiguous declaration.`
);
if (data.style && !isString(data.style)) {
assert.logWarning(
`Invalid 'style' attribute passed to <${sel}> should be a string value, and will be ignored.`,
assert.logError(
`Invalid 'style' attribute passed to <${sel}> is ignored. This attribute must be a string value.`,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should keep this error (which used to be a warning)

vmBeingRendered!.elm
);
}
Expand Down Expand Up @@ -321,10 +321,10 @@ export function ti(value: any): number {
const shouldNormalize = value > 0 && !(isTrue(value) || isFalse(value));
if (process.env.NODE_ENV !== 'production') {
if (shouldNormalize) {
assert.logWarning(
assert.logError(
`Invalid tabindex value \`${toString(
value
)}\` in template for ${vmBeingRendered}. This attribute can only be set to 0 or -1.`,
)}\` in template for ${vmBeingRendered}. This attribute must be set to 0 or -1.`,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should keep this error (which used to be a warning)

vmBeingRendered!.elm
);
}
Expand Down Expand Up @@ -392,8 +392,8 @@ export function c(
`vnode.data.styleMap and vnode.data.style ambiguous declaration.`
);
if (data.style && !isString(data.style)) {
assert.logWarning(
`Invalid 'style' attribute passed to <${sel}> should be a string value, and will be ignored.`,
assert.logError(
`Invalid 'style' attribute passed to <${sel}> is ignored. This attribute must be a string value.`,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should keep this error (which used to be a warning)

vmBeingRendered!.elm
);
}
Expand Down Expand Up @@ -444,10 +444,10 @@ export function i(
markAsDynamicChildren(list);
if (isUndefined(iterable) || iterable === null) {
if (process.env.NODE_ENV !== 'production') {
assert.logWarning(
assert.logError(
`Invalid template iteration for value "${toString(
iterable
)}" in ${vmBeingRendered}, it should be an Array or an iterable Object.`,
)}" in ${vmBeingRendered}. It must be an Array or an iterable Object.`,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should keep this error (which used to be a warning)

vmBeingRendered!.elm
);
}
Expand All @@ -459,7 +459,7 @@ export function i(
isUndefined(iterable[SymbolIterator]),
`Invalid template iteration for value \`${toString(
iterable
)}\` in ${vmBeingRendered}, it requires an array-like object, not \`null\` or \`undefined\`.`
)}\` in ${vmBeingRendered}. It must be an array-like object and not \`null\` nor \`undefined\`.`
);
}
const iterator = iterable[SymbolIterator]();
Expand Down Expand Up @@ -502,15 +502,15 @@ export function i(
if (keyMap[key] === 1 && isUndefined(iterationError)) {
iterationError = `Duplicated "key" attribute value for "<${
childVnode.sel
}>" in ${vmBeingRendered} for item number ${j}. Key with value "${
}>" in ${vmBeingRendered} for item number ${j}. A key with value "${
childVnode.key
}" appears more than once in iteration. Key values must be unique numbers or strings.`;
}" appears more than once in the iteration. Key values must be unique numbers or strings.`;
}
keyMap[key] = 1;
} else if (isUndefined(iterationError)) {
iterationError = `Invalid "key" attribute value in "<${
childVnode.sel
}>" in ${vmBeingRendered} for item number ${j}. Instead set a unique "key" attribute value on all iteration children so internal state can be preserved during rehydration.`;
}>" in ${vmBeingRendered} for item number ${j}. Set a unique "key" value on all iterated child elements.`;
}
}
});
Expand Down Expand Up @@ -681,7 +681,7 @@ export function gid(id: string | undefined | null): string | null | undefined {
if (isUndefined(id) || id === '') {
if (process.env.NODE_ENV !== 'production') {
assert.logError(
`Invalid id value "${id}". Expected a non-empty string.`,
`Invalid id value "${id}". The id attribute must contain a non-empty string.`,
vmBeingRendered!.elm
);
}
Expand All @@ -698,10 +698,12 @@ export function gid(id: string | undefined | null): string | null | undefined {
export function fid(url: string | undefined | null): string | null | undefined {
if (isUndefined(url) || url === '') {
if (process.env.NODE_ENV !== 'production') {
assert.logError(
`Invalid url value "${url}". Expected a non-empty string.`,
vmBeingRendered!.elm
);
if (isUndefined(url)) {
assert.logError(
`Undefined url value for "href" or "xlink:href" attribute. Expected a non-empty string.`,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should keep this error but relax it so that it only cares about undefined

vmBeingRendered!.elm
);
}
}
return url;
}
Expand Down
Loading