-
Notifications
You must be signed in to change notification settings - Fork 438
feat: signals test cases #3962
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
Merged
Merged
feat: signals test cases #3962
Changes from 9 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
770670f
chore: initial set of test cases for signals implementation
jmsjtu 4439e74
chore: revert playground changes
jmsjtu 81e4a1a
Merge branch 'jtu/signals' of github.com:salesforce/lwc into jtu/sign…
jmsjtu e4d348f
Merge branch 'jtu/signals' of github.com:salesforce/lwc into jtu/sign…
jmsjtu 1c736a7
chore: move unit tests to @lwc/signals package
jmsjtu a81647d
Merge branch 'jtu/signals' of github.com:salesforce/lwc into jtu/sign…
jmsjtu e161656
chore: update config for signals feature flag
jmsjtu a278dda
chore: update tests
jmsjtu 8019c24
Merge branch 'jtu/signals' of github.com:salesforce/lwc into jtu/sign…
jmsjtu d140646
Merge branch 'jtu/signals' of github.com:salesforce/lwc into jtu/sign…
jmsjtu 1013936
chore: update based on pr feedback
jmsjtu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
220 changes: 220 additions & 0 deletions
220
packages/@lwc/integration-karma/test/signal/protocol/index.spec.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,220 @@ | ||
| import { createElement, setFeatureFlagForTest } from 'lwc'; | ||
| import Reactive from 'x/reactive'; | ||
| import NonReactive from 'x/nonReactive'; | ||
| import Container from 'x/container'; | ||
| import Parent from 'x/parent'; | ||
| import Child from 'x/child'; | ||
| import DuplicateSignalOnTemplate from 'x/duplicateSignalOnTemplate'; | ||
| import List from 'x/list'; | ||
| import { Signal } from 'x/signal'; | ||
|
|
||
| if (process.env.ENABLE_EXPERIMENTAL_SIGNALS) { | ||
| describe('signal protocol', () => { | ||
| beforeAll(() => { | ||
| setFeatureFlagForTest('ENABLE_EXPERIMENTAL_SIGNALS', true); | ||
| }); | ||
|
|
||
| afterAll(() => { | ||
| setFeatureFlagForTest('ENABLE_EXPERIMENTAL_SIGNALS', false); | ||
| }); | ||
|
|
||
| describe('lwc engine subscribes template re-render callback when signal is bound to an LWC and used on a template', () => { | ||
| [ | ||
| { | ||
| testName: | ||
| 'contains a getter that references a bound signal (.value on template)', | ||
| flag: 'showGetterSignal', | ||
| }, | ||
| { | ||
| testName: 'contains a getter that references a bound signal value', | ||
| flag: 'showOnlyUsingSignalNotValue', | ||
| }, | ||
| { | ||
| testName: 'contains a signal with @api annotation (.value on template)', | ||
| flag: 'showApiSignal', | ||
| }, | ||
| { | ||
| testName: 'contains a signal with @track annotation (.value on template)', | ||
| flag: 'showTrackedSignal', | ||
| }, | ||
| { | ||
| testName: | ||
| 'contains an observed field referencing a signal (.value on template)', | ||
| flag: 'showObservedFieldSignal', | ||
| }, | ||
| { | ||
| testName: | ||
| 'contains a direct reference to a signal (not .value) in the template', | ||
| flag: 'showOnlyUsingSignalNotValue', | ||
| }, | ||
| ].forEach(({ testName, flag }) => { | ||
| // Test all ways of binding signal to an LWC + template that cause re-rendering | ||
| it(testName, async () => { | ||
| const elm = createElement('x-reactive', { is: Reactive }); | ||
| document.body.appendChild(elm); | ||
| await Promise.resolve(); | ||
|
|
||
| expect(elm.getSignalSubscriberCount()).toBe(0); | ||
| elm[flag] = true; | ||
| await Promise.resolve(); | ||
|
|
||
| // the engine will automatically subscribe the re-render callback | ||
| expect(elm.getSignalSubscriberCount()).toBe(1); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| it('lwc engine should automatically unsubscribe the re-render callback if signal is not used on a template', async () => { | ||
| const elm = createElement('x-reactive', { is: Reactive }); | ||
| elm.showObservedFieldSignal = true; | ||
| document.body.appendChild(elm); | ||
| await Promise.resolve(); | ||
|
|
||
| expect(elm.getSignalSubscriberCount()).toBe(1); | ||
| elm.showObservedFieldSignal = false; | ||
| await Promise.resolve(); | ||
|
|
||
| expect(elm.getSignalSubscriberCount()).toBe(0); | ||
| document.body.removeChild(elm); | ||
| }); | ||
|
|
||
| it('lwc engine does not subscribe re-render callback if signal is not used on a template', async () => { | ||
| const elm = createElement('x-non-reactive', { is: NonReactive }); | ||
| document.body.appendChild(elm); | ||
| await Promise.resolve(); | ||
|
|
||
| expect(elm.getSignalSubscriberCount()).toBe(0); | ||
| }); | ||
|
|
||
| it('only the components referencing a signal should re-render', async () => { | ||
| const container = createElement('x-container', { is: Container }); | ||
| const signalElm = createElement('x-signal-elm', { is: Child }); | ||
| const signal = new Signal('initial value'); | ||
| signalElm.signal = signal; | ||
| container.appendChild(signalElm); | ||
| document.body.appendChild(container); | ||
|
|
||
| await Promise.resolve(); | ||
|
|
||
| expect(container.renderCount).toBe(1); | ||
| expect(signalElm.renderCount).toBe(1); | ||
| expect(signal.getSubscriberCount()).toBe(1); | ||
|
|
||
| signal.value = 'updated value'; | ||
| await Promise.resolve(); | ||
|
|
||
| expect(container.renderCount).toBe(1); | ||
| expect(signalElm.renderCount).toBe(2); | ||
| expect(signal.getSubscriberCount()).toBe(1); | ||
| }); | ||
|
|
||
| it('only subscribes the re-render callback a single time when signal is referenced multiple times on a template', async () => { | ||
| const elm = createElement('x-duplicate-signals-on-template', { | ||
| is: DuplicateSignalOnTemplate, | ||
| }); | ||
| document.body.appendChild(elm); | ||
| await Promise.resolve(); | ||
|
|
||
| expect(elm.renderCount).toBe(1); | ||
| expect(elm.getSignalSubscriberCount()).toBe(1); | ||
| expect(elm.getSignalRemovedSubscriberCount()).toBe(0); | ||
|
|
||
| elm.updateSignalValue(); | ||
| await Promise.resolve(); | ||
|
|
||
| expect(elm.renderCount).toBe(2); | ||
| expect(elm.getSignalSubscriberCount()).toBe(1); | ||
| expect(elm.getSignalRemovedSubscriberCount()).toBe(1); | ||
| }); | ||
|
|
||
| it('only subscribes re-render callback a single time when signal is referenced multiple times in a list', async () => { | ||
| const elm = createElement('x-list', { is: List }); | ||
| const signal = new Signal('initial value'); | ||
| elm.signal = signal; | ||
| document.body.appendChild(elm); | ||
| await Promise.resolve(); | ||
|
|
||
| expect(signal.getSubscriberCount()).toBe(1); | ||
| expect(signal.getRemovedSubscriberCount()).toBe(0); | ||
|
|
||
| document.body.removeChild(elm); | ||
| await Promise.resolve(); | ||
|
|
||
| expect(signal.getSubscriberCount()).toBe(0); | ||
| expect(signal.getRemovedSubscriberCount()).toBe(1); | ||
| }); | ||
|
|
||
| it('unsubscribes when element is removed from the dom', async () => { | ||
| const elm = createElement('x-child', { is: Child }); | ||
| const signal = new Signal('initial value'); | ||
| elm.signal = signal; | ||
| document.body.appendChild(elm); | ||
| await Promise.resolve(); | ||
|
|
||
| expect(signal.getSubscriberCount()).toBe(1); | ||
| expect(signal.getRemovedSubscriberCount()).toBe(0); | ||
|
|
||
| document.body.removeChild(elm); | ||
| await Promise.resolve(); | ||
|
|
||
| expect(signal.getSubscriberCount()).toBe(0); | ||
| expect(signal.getRemovedSubscriberCount()).toBe(1); | ||
| }); | ||
|
|
||
| it('on template re-render unsubscribes all components where signal is not present on the template', async () => { | ||
| const elm = createElement('x-parent', { is: Parent }); | ||
| elm.showChild = true; | ||
|
|
||
| document.body.appendChild(elm); | ||
| await Promise.resolve(); | ||
|
|
||
| // subscribed both parent and child | ||
| // as long as parent contains reference to the signal, even if it's just to pass it to a child | ||
| // it will be subscribed. | ||
| expect(elm.getSignalSubscriberCount()).toBe(2); | ||
| expect(elm.getSignalRemovedSubscriberCount()).toBe(0); | ||
|
|
||
| elm.showChild = false; | ||
| await Promise.resolve(); | ||
|
|
||
| // The signal is not being used on the parent template anymore so it will be removed | ||
| expect(elm.getSignalSubscriberCount()).toBe(0); | ||
| expect(elm.getSignalRemovedSubscriberCount()).toBe(2); | ||
| }); | ||
|
|
||
| it('does not subscribe if the signal shape is incorrect', async () => { | ||
| const elm = createElement('x-child', { is: Child }); | ||
| const subscribe = jasmine.createSpy(); | ||
| // Note the signals property is value's' and not value | ||
| const signal = { values: 'initial value', subscribe }; | ||
| elm.signal = signal; | ||
| document.body.appendChild(elm); | ||
| await Promise.resolve(); | ||
|
|
||
| expect(subscribe).not.toHaveBeenCalled(); | ||
| }); | ||
| }); | ||
| } else { | ||
| describe('ENABLE_EXPERIMENTAL_SIGNALS not set', () => { | ||
| beforeAll(() => { | ||
| setFeatureFlagForTest('ENABLE_EXPERIMENTAL_SIGNALS', false); | ||
| }); | ||
|
|
||
| it('does not subscribe or unsubscribe if feature flag is disabled', async () => { | ||
| const elm = createElement('x-child', { is: Child }); | ||
| const signal = new Signal('initial value'); | ||
| elm.signal = signal; | ||
| document.body.appendChild(elm); | ||
| await Promise.resolve(); | ||
|
|
||
| expect(signal.getSubscriberCount()).toBe(0); | ||
| expect(signal.getRemovedSubscriberCount()).toBe(0); | ||
|
|
||
| document.body.removeChild(elm); | ||
| await Promise.resolve(); | ||
|
|
||
| expect(signal.getSubscriberCount()).toBe(0); | ||
| expect(signal.getRemovedSubscriberCount()).toBe(0); | ||
| }); | ||
| }); | ||
| } |
3 changes: 3 additions & 0 deletions
3
packages/@lwc/integration-karma/test/signal/protocol/x/child/child.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| <template> | ||
| {signal.value} | ||
| </template> |
10 changes: 10 additions & 0 deletions
10
packages/@lwc/integration-karma/test/signal/protocol/x/child/child.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import { LightningElement, api } from 'lwc'; | ||
|
|
||
| export default class extends LightningElement { | ||
| @api renderCount = 0; | ||
| @api signal; | ||
|
|
||
| renderedCallback() { | ||
| this.renderCount++; | ||
| } | ||
| } |
3 changes: 3 additions & 0 deletions
3
packages/@lwc/integration-karma/test/signal/protocol/x/container/container.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| <template> | ||
| <slot></slot> | ||
| </template> |
9 changes: 9 additions & 0 deletions
9
packages/@lwc/integration-karma/test/signal/protocol/x/container/container.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import { LightningElement, api } from 'lwc'; | ||
|
|
||
| export default class extends LightningElement { | ||
| @api renderCount = 0; | ||
|
|
||
| renderedCallback() { | ||
| this.renderCount++; | ||
| } | ||
| } |
5 changes: 5 additions & 0 deletions
5
...ion-karma/test/signal/protocol/x/duplicateSignalOnTemplate/duplicateSignalOnTemplate.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| <template> | ||
| {signal.value} | ||
| {signal.value} | ||
| {signal.value} | ||
| </template> |
26 changes: 26 additions & 0 deletions
26
...ation-karma/test/signal/protocol/x/duplicateSignalOnTemplate/duplicateSignalOnTemplate.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import { LightningElement, api } from 'lwc'; | ||
| import { Signal } from 'x/signal'; | ||
|
|
||
| export default class extends LightningElement { | ||
| signal = new Signal('initial value'); | ||
| @api renderCount = 0; | ||
|
|
||
| renderedCallback() { | ||
| this.renderCount++; | ||
| } | ||
|
|
||
| @api | ||
| getSignalSubscriberCount() { | ||
| return this.signal.getSubscriberCount(); | ||
| } | ||
|
|
||
| @api | ||
| getSignalRemovedSubscriberCount() { | ||
| return this.signal.getRemovedSubscriberCount(); | ||
| } | ||
|
|
||
| @api | ||
| updateSignalValue() { | ||
| this.signal.value = 'updated value'; | ||
| } | ||
| } |
5 changes: 5 additions & 0 deletions
5
packages/@lwc/integration-karma/test/signal/protocol/x/list/list.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| <template> | ||
| <template for:each={items} for:item="item"> | ||
| <p key={item}>{signal.value}</p> | ||
| </template> | ||
| </template> |
6 changes: 6 additions & 0 deletions
6
packages/@lwc/integration-karma/test/signal/protocol/x/list/list.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| import { LightningElement, api } from 'lwc'; | ||
|
|
||
| export default class extends LightningElement { | ||
| @api signal; | ||
| items = [1, 2, 3, 4, 5, 6]; | ||
| } |
7 changes: 7 additions & 0 deletions
7
packages/@lwc/integration-karma/test/signal/protocol/x/nonReactive/nonReactive.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| <template> | ||
| <span>{apiSignalValue}</span> | ||
| <span>{trackSignalValue}</span> | ||
| <span>{observedFieldSignalValue}</span> | ||
| <span>{externalSignalValueGetter}</span> | ||
| <span>{observedFieldBoundSignalValue}</span> | ||
| </template> |
22 changes: 22 additions & 0 deletions
22
packages/@lwc/integration-karma/test/signal/protocol/x/nonReactive/nonReactive.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import { LightningElement, api, track } from 'lwc'; | ||
| import { Signal } from 'x/signal'; | ||
|
|
||
| const signal = new Signal('initial value'); | ||
|
|
||
| export default class extends LightningElement { | ||
| // Note that this signal is bound but it's never referenced on the template | ||
| _signal = signal; | ||
| @api apiSignalValue = signal.value; | ||
| @track trackSignalValue = signal.value; | ||
| observedFieldExternalSignalValue = signal.value; | ||
| observedFieldBoundSignalValue = this._signal.value; | ||
|
|
||
| get externalSignalValueGetter() { | ||
| return signal.value; | ||
| } | ||
|
|
||
| @api | ||
| getSignalSubscriberCount() { | ||
| return signal.getSubscriberCount(); | ||
| } | ||
| } |
3 changes: 3 additions & 0 deletions
3
packages/@lwc/integration-karma/test/signal/protocol/x/parent/parent.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| <template> | ||
| <x-child lwc:if={showChild} signal={signal}></x-child> | ||
| </template> |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.