Skip to content

Commit 58cbe77

Browse files
authored
fix(wire): connect wire before calling connectedCallback @W-18205007 (#5322)
* test(ssr): add LWR's lwcRuntimeFlags test * test(util): fix order of expected/actual * test(wire): simplify test to focus on broken case * fix(wire): connect wire before calling connectedCallback
1 parent dc11f9e commit 58cbe77

File tree

8 files changed

+57
-20
lines changed

8 files changed

+57
-20
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"entry": "x/component"
3+
}

packages/@lwc/engine-server/src/__tests__/fixtures/wire/adapter-lifecycle/error.txt

Whitespace-only changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<fixture-test>
2+
connect, update(hello)
3+
</fixture-test>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<template lwc:render-mode="light">
2+
{connectedCallbackProp}
3+
</template>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { LightningElement, wire } from 'lwc';
2+
import adapter from './wire';
3+
4+
export default class extends LightningElement {
5+
static renderMode = 'light';
6+
@wire(adapter, { value: 'hello' })
7+
data;
8+
9+
connectedCallback() {
10+
this.connectedCallbackProp = this.data.join(', ');
11+
}
12+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export default class {
2+
constructor(callback) {
3+
this.callback = callback;
4+
this.state = [];
5+
}
6+
7+
update(config) {
8+
this.state.push(`update(${config.value})`);
9+
this.callback([...this.state]);
10+
}
11+
12+
connect() {
13+
this.state.push('connect');
14+
this.callback([...this.state]);
15+
}
16+
17+
disconnect() {
18+
this.state.push('disconnect');
19+
}
20+
}

packages/@lwc/ssr-compiler/src/compile-js/generate-markup.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@ const bGenerateMarkup = esTemplate`
5252
attrs,
5353
__lwcPublicProperties__
5454
);
55+
${/*connect wire*/ is.statement}
5556
instance.isConnected = true;
5657
if (instance.connectedCallback) {
5758
__mutationTracker.enable(instance);
5859
instance.connectedCallback();
5960
__mutationTracker.disable(instance);
6061
}
61-
${/*connect wire*/ is.statement}
6262
// If a render() function is defined on the class or any of its superclasses, then that takes priority.
6363
// Next, if the class or any of its superclasses has an implicitly-associated template, then that takes
6464
// second priority (e.g. a foo.html file alongside a foo.js file). Finally, there is a fallback empty template.

scripts/test-utils/html-snapshot-matcher.ts

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
*/
2020

2121
import * as fs from 'node:fs/promises';
22+
import { AssertionError } from 'node:assert';
2223
import { equals, type Assertion, type ChaiPlugin, type ExpectStatic } from '@vitest/expect';
2324
import { swapLwcStyleForStyleTag } from './swap-lwc-style-for-style';
2425

@@ -29,18 +30,11 @@ function createMismatchError<T = unknown>(
2930
expected: T
3031
) {
3132
// TODO [W-17971915]: clean this up, potentially using AssertionError
32-
const error = new Error(message);
33-
Object.defineProperty(error, 'actual', {
34-
value: actual,
35-
enumerable: true,
36-
configurable: true,
37-
writable: true,
38-
});
39-
Object.defineProperty(error, 'expected', {
40-
value: expected,
41-
enumerable: true,
42-
configurable: true,
43-
writable: true,
33+
// DONE
34+
const error = new AssertionError({
35+
message,
36+
actual,
37+
expected,
4438
});
4539
Object.defineProperty(error, 'diffOptions', { value: { expand } });
4640
return error;
@@ -70,30 +64,32 @@ export const HtmlSnapshotPlugin: ChaiPlugin = (chai, utils) => {
7064
async function (this: Assertion, htmlFilePath: string, expect: ExpectStatic) {
7165
utils.flag(this, '_name', 'toMatchHtmlSnapshot');
7266

73-
const expectedHtml = utils.flag(this, 'object');
67+
const actualHtml = utils.flag(this, 'object');
7468
const isNot = utils.flag(this, 'negate');
7569
if (isNot) {
7670
throw new Error('toMatchHtmlSnapshot cannot be used with "not"');
7771
}
7872

7973
// Leverage the built-in mechanism to write new fixtures to disk.
8074
if (!(await fileExists(htmlFilePath))) {
81-
return await expect(expectedHtml).toMatchFileSnapshot(htmlFilePath);
75+
return await expect(actualHtml).toMatchFileSnapshot(htmlFilePath);
8276
}
8377

84-
const actualHtml = await fs.readFile(htmlFilePath, 'utf8');
85-
const actualHtmlCompatWithSSRv1 = swapLwcStyleForStyleTag(actualHtml);
78+
const expectedHtmlRaw = await fs.readFile(htmlFilePath, 'utf8');
79+
// SSR v2 uses <lwc-style> elements, but SSR v1 only uses <style>, so we have to
80+
// normalize the output in order to reuse the same snapshots for both packages
81+
const expectedHtmlNormalized = swapLwcStyleForStyleTag(expectedHtmlRaw);
8682

8783
// This isn't strictly necessary because we await the assertion in `test-fixture-dir.ts`.
8884
// However, it mimics the behavior exhibited by `toMatchFileSnapshot` such that, if the
8985
// assertion were not awaited, any failed assertions would be queued up and reported
9086
// asynchronously after the test completed.
91-
if (!equals(actualHtmlCompatWithSSRv1, expectedHtml)) {
87+
if (!equals(expectedHtmlNormalized, actualHtml)) {
9288
throw createMismatchError(
9389
'HTML snapshot mismatch',
9490
true,
95-
actualHtmlCompatWithSSRv1,
96-
expectedHtml
91+
actualHtml,
92+
expectedHtmlNormalized
9793
);
9894
}
9995
}

0 commit comments

Comments
 (0)