Skip to content

Commit d50fb45

Browse files
committed
Added initLiveComponent to much more easily and reliably set the data-live-data-value attribute
1 parent 92429e4 commit d50fb45

File tree

6 files changed

+50
-80
lines changed

6 files changed

+50
-80
lines changed

src/LiveComponent/assets/test/controller/action.test.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,15 @@
1010
'use strict';
1111

1212
import { clearDOM } from '@symfony/stimulus-testing';
13-
import { startStimulus } from '../tools';
13+
import { initLiveComponent, startStimulus } from '../tools';
1414
import { getByLabelText, getByText, waitFor } from '@testing-library/dom';
1515
import userEvent from '@testing-library/user-event';
1616
import fetchMock from 'fetch-mock-jest';
1717

1818
describe('LiveController Action Tests', () => {
1919
const template = (data) => `
2020
<div
21-
data-controller="live"
22-
data-live-url-value="http://localhost/_components/my_component"
21+
${initLiveComponent('/_components/my_component', data)}
2322
>
2423
<label>
2524
Comments:
@@ -46,10 +45,7 @@ describe('LiveController Action Tests', () => {
4645

4746
it('Sends an action and cancels any re-renders', async () => {
4847
const data = { comments: 'hi' };
49-
const { element } = await startStimulus(
50-
template(data),
51-
data
52-
);
48+
const { element } = await startStimulus(template(data));
5349

5450
// ONLY a post is sent, not a re-render GET
5551
const postMock = fetchMock.postOnce('http://localhost/_components/my_component/save', {

src/LiveComponent/assets/test/controller/basic.test.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ describe('LiveController Basic Tests', () => {
2626
})
2727
const { element } = await startStimulus(
2828
'<div data-controller="live"></div>',
29-
{},
3029
container
3130
);
3231

src/LiveComponent/assets/test/controller/csrf.test.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,14 @@
1010
'use strict';
1111

1212
import { clearDOM } from '@symfony/stimulus-testing';
13-
import { startStimulus } from '../tools';
14-
import { getByLabelText, getByText, waitFor } from '@testing-library/dom';
13+
import { initLiveComponent, startStimulus } from '../tools';
14+
import { getByText, waitFor } from '@testing-library/dom';
1515
import fetchMock from 'fetch-mock-jest';
1616

1717
describe('LiveController CSRF Tests', () => {
1818
const template = (data) => `
1919
<div
20-
data-controller="live"
21-
data-live-url-value="http://localhost/_components/my_component"
20+
${initLiveComponent('/_components/my_component', data)}
2221
data-live-csrf-value="123TOKEN"
2322
>
2423
<label>
@@ -46,10 +45,7 @@ describe('LiveController CSRF Tests', () => {
4645

4746
it('Sends the CSRF token on an action', async () => {
4847
const data = { comments: 'hi' };
49-
const { element } = await startStimulus(
50-
template(data),
51-
data
52-
);
48+
const { element } = await startStimulus(template(data));
5349

5450
const postMock = fetchMock.postOnce('http://localhost/_components/my_component/save', {
5551
html: template({ comments: 'hi', isSaved: true }),

src/LiveComponent/assets/test/controller/model.test.js

Lines changed: 18 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,15 @@
1010
'use strict';
1111

1212
import { clearDOM } from '@symfony/stimulus-testing';
13-
import { startStimulus } from '../tools';
13+
import { initLiveComponent, startStimulus } from '../tools';
1414
import { getByLabelText, waitFor } from '@testing-library/dom';
1515
import userEvent from '@testing-library/user-event';
1616
import fetchMock from 'fetch-mock-jest';
1717

1818
describe('LiveController data-model Tests', () => {
1919
const template = (data) => `
2020
<div
21-
data-controller="live"
22-
data-live-url-value="http://localhost"
21+
${initLiveComponent('/_components/my_component', data)}
2322
>
2423
<label>
2524
Name:
@@ -39,12 +38,9 @@ describe('LiveController data-model Tests', () => {
3938

4039
it('renders correctly with data-model and live#update', async () => {
4140
const data = { name: 'Ryan' };
42-
const { element, controller } = await startStimulus(
43-
template(data),
44-
data
45-
);
41+
const { element, controller } = await startStimulus(template(data));
4642

47-
fetchMock.getOnce('http://localhost/?name=Ryan+WEAVER', {
43+
fetchMock.getOnce('end:?name=Ryan+WEAVER', {
4844
html: template({ name: 'Ryan Weaver' }),
4945
data: { name: 'Ryan Weaver' }
5046
});
@@ -67,10 +63,7 @@ describe('LiveController data-model Tests', () => {
6763

6864
it('correctly only uses the most recent render call results', async () => {
6965
const data = { name: 'Ryan' };
70-
const { element, controller } = await startStimulus(
71-
template(data),
72-
data
73-
);
66+
const { element, controller } = await startStimulus(template(data));
7467

7568
let renderCount = 0;
7669
element.addEventListener('live:render', () => {
@@ -83,7 +76,7 @@ describe('LiveController data-model Tests', () => {
8376
['guy', 150]
8477
];
8578
requests.forEach(([string, delay]) => {
86-
fetchMock.getOnce(`http://localhost/?name=Ryan${string}`, {
79+
fetchMock.getOnce(`end:my_component?name=Ryan${string}`, {
8780
// the _ at the end helps us look that the input has changed
8881
// as a result of a re-render (not just from typing in the input)
8982
html: template({ name: `Ryan${string}_` }),
@@ -114,17 +107,14 @@ describe('LiveController data-model Tests', () => {
114107

115108
it('falls back to using the name attribute when no data-model is present', async () => {
116109
const data = { name: 'Ryan' };
117-
const { element, controller } = await startStimulus(
118-
template(data),
119-
data
120-
);
110+
const { element, controller } = await startStimulus(template(data));
121111

122112
// replace data-model with name attribute
123113
const inputElement = getByLabelText(element, 'Name:');
124114
delete inputElement.dataset.model;
125115
inputElement.setAttribute('name', 'name');
126116

127-
fetchMock.getOnce('http://localhost/?name=Ryan+WEAVER', {
117+
fetchMock.getOnce('end:?name=Ryan+WEAVER', {
128118
html: template({ name: 'Ryan Weaver' }),
129119
data: { name: 'Ryan Weaver' }
130120
});
@@ -140,17 +130,14 @@ describe('LiveController data-model Tests', () => {
140130

141131
it('uses data-model when both name and data-model is present', async () => {
142132
const data = { name: 'Ryan' };
143-
const { element, controller } = await startStimulus(
144-
template(data),
145-
data
146-
);
133+
const { element, controller } = await startStimulus(template(data));
147134

148135
// give element data-model="name" and name="first_name"
149136
const inputElement = getByLabelText(element, 'Name:');
150137
inputElement.setAttribute('name', 'first_name');
151138

152139
// ?name should be what's sent to the server
153-
fetchMock.getOnce('http://localhost/?name=Ryan+WEAVER', {
140+
fetchMock.getOnce('end:?name=Ryan+WEAVER', {
154141
html: template({ name: 'Ryan Weaver' }),
155142
data: { name: 'Ryan Weaver' }
156143
});
@@ -166,8 +153,7 @@ describe('LiveController data-model Tests', () => {
166153
it('standardizes user[firstName] style models into post.name', async () => {
167154
const deeperModelTemplate = (data) => `
168155
<div
169-
data-controller="live"
170-
data-live-url-value="http://localhost"
156+
${initLiveComponent('/_components/my_component', data)}
171157
>
172158
<label>
173159
First Name:
@@ -180,16 +166,13 @@ describe('LiveController data-model Tests', () => {
180166
</div>
181167
`;
182168
const data = { user: { firstName: 'Ryan' } };
183-
const { element, controller } = await startStimulus(
184-
deeperModelTemplate(data),
185-
data
186-
);
169+
const { element, controller } = await startStimulus(deeperModelTemplate(data));
187170

188171
// replace data-model with name attribute
189172
const inputElement = getByLabelText(element, 'First Name:');
190173

191174
const newData = { user: { firstName: 'Ryan Weaver' } };
192-
fetchMock.getOnce('http://localhost/?user%5BfirstName%5D=Ryan+WEAVER', {
175+
fetchMock.getOnce('end:?user%5BfirstName%5D=Ryan+WEAVER', {
193176
html: deeperModelTemplate(newData),
194177
data: newData
195178
});
@@ -206,8 +189,7 @@ describe('LiveController data-model Tests', () => {
206189
it('updates correctly when live#update is on a parent element', async () => {
207190
const parentUpdateTemplate = (data) => `
208191
<div
209-
data-controller="live"
210-
data-live-url-value="http://localhost"
192+
${initLiveComponent('/_components/my_component', data)}
211193
>
212194
<div data-action="input->live#update">
213195
<label>
@@ -222,12 +204,9 @@ describe('LiveController data-model Tests', () => {
222204
`;
223205

224206
const data = { firstName: 'Ryan' };
225-
const { element, controller } = await startStimulus(
226-
parentUpdateTemplate(data),
227-
data
228-
);
207+
const { element } = await startStimulus(parentUpdateTemplate(data));
229208

230-
fetchMock.getOnce('http://localhost/?firstName=Ryan+WEAVER', {
209+
fetchMock.getOnce('end:?firstName=Ryan+WEAVER', {
231210
html: parentUpdateTemplate({ firstName: 'Ryan Weaver' }),
232211
data: { firstName: 'Ryan Weaver' }
233212
});
@@ -246,12 +225,9 @@ describe('LiveController data-model Tests', () => {
246225
it('tracks which fields should be modified, sends, without forgetting previous fields', async () => {
247226
// start with one other field in validatedFields
248227
const data = { name: 'Ryan', validatedFields: ['otherField'] };
249-
const { element, controller } = await startStimulus(
250-
template(data),
251-
data
252-
);
228+
const { element } = await startStimulus(template(data));
253229

254-
fetchMock.getOnce('http://localhost/?name=Ryan+WEAVER&validatedFields%5B0%5D=otherField&validatedFields%5B1%5D=name', {
230+
fetchMock.getOnce('end:?name=Ryan+WEAVER&validatedFields%5B0%5D=otherField&validatedFields%5B1%5D=name', {
255231
html: template({ name: 'Ryan Weaver' }),
256232
data: { name: 'Ryan Weaver' }
257233
});

src/LiveComponent/assets/test/controller/render.test.js

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,15 @@
1010
'use strict';
1111

1212
import { clearDOM } from '@symfony/stimulus-testing';
13-
import { startStimulus } from '../tools';
13+
import { initLiveComponent, startStimulus } from '../tools';
1414
import { createEvent, fireEvent, getByLabelText, getByText, waitFor } from '@testing-library/dom';
1515
import userEvent from '@testing-library/user-event';
1616
import fetchMock from 'fetch-mock-jest';
1717

1818
describe('LiveController rendering Tests', () => {
1919
const template = (data, includeLoading = false) => `
2020
<div
21-
data-controller="live"
22-
data-live-url-value="http://localhost/_components/my_component"
21+
${initLiveComponent('/_components/my_component', data)}
2322
>
2423
<!-- form field not mapped with data-model -->
2524
<label>
@@ -46,10 +45,7 @@ describe('LiveController rendering Tests', () => {
4645

4746
it('renders from the AJAX endpoint & updates data', async () => {
4847
const data = { name: 'Ryan' };
49-
const { element, controller } = await startStimulus(
50-
template(data),
51-
data
52-
);
48+
const { element, controller } = await startStimulus(template(data));
5349

5450
fetchMock.get('http://localhost/_components/my_component?name=Ryan', {
5551
html: '<div>aloha!</div>',
@@ -63,10 +59,7 @@ describe('LiveController rendering Tests', () => {
6359

6460
it('conserves values of fields modified after a render request', async () => {
6561
const data = { name: 'Ryan' };
66-
const { element } = await startStimulus(
67-
template(data),
68-
data
69-
);
62+
const { element } = await startStimulus(template(data));
7063

7164
fetchMock.get('http://localhost/_components/my_component?name=Ryan', {
7265
html: template({ name: 'Kevin' }),
@@ -88,8 +81,7 @@ describe('LiveController rendering Tests', () => {
8881
// "true" gives the comment input a loading behavior
8982
// this could make the input.isEqualNode() be false when comparing
9083
// that's exactly what we want test for
91-
template(data, true),
92-
data
84+
template(data, true)
9385
);
9486

9587
fetchMock.get('http://localhost/_components/my_component?name=Ryan', {
@@ -108,12 +100,9 @@ describe('LiveController rendering Tests', () => {
108100

109101
it('cancels a re-render if the page is navigating away', async () => {
110102
const data = { name: 'Ryan' };
111-
const { element, controller } = await startStimulus(
112-
template(data),
113-
data
114-
);
103+
const { element } = await startStimulus(template(data));
115104

116-
fetchMock.get('http://localhost/_components/my_component?name=Ryan', {
105+
fetchMock.get('end:?name=Ryan', {
117106
html: '<div>aloha!</div>',
118107
data: { name: 'Kevin' }
119108
}, {

src/LiveComponent/assets/test/tools.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const TestData = class {
1111

1212
let application;
1313

14-
const startStimulus = async (html, data, container = null) => {
14+
const startStimulus = async (html, container = null) => {
1515
// start the Stimulus app just once per test suite
1616
if (!application) {
1717
application = Application.start();
@@ -26,8 +26,6 @@ const startStimulus = async (html, data, container = null) => {
2626
document.body.appendChild(container);
2727

2828
const element = getControllerElement(container);
29-
element.dataset.liveDataValue = JSON.stringify(data);
30-
3129
await waitFor(() => application.getControllerForElementAndIdentifier(element, 'live'));
3230
const controller = application.getControllerForElementAndIdentifier(element, 'live');
3331

@@ -38,4 +36,20 @@ const getControllerElement = (container) => {
3836
return container.querySelector('[data-controller="live"]');
3937
};
4038

41-
export { startStimulus, getControllerElement };
39+
const dataToJsonAttribute = (data) => {
40+
const container = document.createElement('div');
41+
container.dataset.foo = JSON.stringify(data);
42+
43+
// returns the now-escaped string, ready to be used in an HTML attribute
44+
return container.outerHTML.match(/data\-foo="(.+)\"/)[1]
45+
}
46+
47+
const initLiveComponent = (url, data) => {
48+
return `
49+
data-controller="live"
50+
data-live-url-value="http://localhost${url}"
51+
data-live-data-value="${dataToJsonAttribute(data)}"
52+
`;
53+
}
54+
55+
export { startStimulus, getControllerElement, initLiveComponent };

0 commit comments

Comments
 (0)