preact-render-to-string 3.7.1 → 3.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/test/render.js DELETED
@@ -1,571 +0,0 @@
1
- import { render, shallowRender } from '../src';
2
- import { h, Component } from 'preact';
3
- import chai, { expect } from 'chai';
4
- import { spy, stub, match } from 'sinon';
5
- import sinonChai from 'sinon-chai';
6
- chai.use(sinonChai);
7
-
8
- describe('render', () => {
9
- describe('Basic JSX', () => {
10
- it('should render JSX', () => {
11
- let rendered = render(<div class="foo">bar</div>),
12
- expected = `<div class="foo">bar</div>`;
13
-
14
- expect(rendered).to.equal(expected);
15
- });
16
-
17
- describe('whitespace', () => {
18
- it('should omit whitespace between elements', () => {
19
- let children = [];
20
- for (let i=0; i<1000; i++) {
21
- children.push(Math.random()>.5 ? String(i) : h('x-'+String(i), null, i));
22
- }
23
- let rendered = render(
24
- <div class="foo">
25
- x
26
- <a>a</a>
27
- <b>b</b>
28
- c
29
- {children}
30
- d
31
- </div>
32
- );
33
-
34
- expect(rendered).not.to.contain(/\s/);
35
- });
36
-
37
- it('should not indent when attributes contain newlines', () => {
38
- let rendered = render(
39
- <div class={`foo\n\tbar\n\tbaz`}>
40
- <a>a</a>
41
- <b>b</b>
42
- c
43
- </div>
44
- );
45
-
46
- expect(rendered).to.equal(`<div class="foo\n\tbar\n\tbaz"><a>a</a><b>b</b>c</div>`);
47
- });
48
- });
49
-
50
- it('should omit falsey attributes', () => {
51
- let rendered = render(<div a={null} b={undefined} c={false} />),
52
- expected = `<div></div>`;
53
-
54
- expect(rendered).to.equal(expected);
55
-
56
- expect(render(<div foo={0} />)).to.equal(`<div foo="0"></div>`);
57
- });
58
-
59
- describe('attribute name sanitization', () => {
60
- it('should omit attributes with invalid names', () => {
61
- let rendered = render(h('div', {
62
- '<a': '1',
63
- 'a>': '1',
64
- 'foo"bar': '1',
65
- '"hello"': '1'
66
- }));
67
- expect(rendered).to.equal(`<div></div>`);
68
- });
69
-
70
- it('should mitigate attribute name injection', () => {
71
- let rendered = render(h('div', {
72
- '></div><script>alert("hi")</script>': '',
73
- 'foo onclick': 'javascript:alert()',
74
- a: 'b'
75
- }));
76
- expect(rendered).to.equal(`<div a="b"></div>`);
77
- });
78
-
79
- it('should allow emoji attribute names', () => {
80
- let rendered = render(h('div', {
81
- 'a;b': '1',
82
- 'a🧙‍b': '1'
83
- }));
84
- expect(rendered).to.equal(`<div a;b="1" a🧙‍b="1"></div>`);
85
- });
86
- });
87
-
88
- it('should collapse collapsible attributes', () => {
89
- let rendered = render(<div class="" style="" foo={true} bar />),
90
- expected = `<div class style foo bar></div>`;
91
-
92
- expect(rendered).to.equal(expected);
93
- });
94
-
95
- it('should omit functions', () => {
96
- let rendered = render(<div a={()=>{}} b={function(){}} />),
97
- expected = `<div></div>`;
98
-
99
- expect(rendered).to.equal(expected);
100
- });
101
-
102
- it('should encode entities', () => {
103
- let rendered = render(<div a={'"<>&'}>{'"<>&'}</div>),
104
- expected = `<div a="&quot;&lt;&gt;&amp;">&quot;&lt;&gt;&amp;</div>`;
105
-
106
- expect(rendered).to.equal(expected);
107
- });
108
-
109
- it('should omit falsey children', () => {
110
- let rendered = render(<div>{null}|{undefined}|{false}</div>),
111
- expected = `<div>||</div>`;
112
-
113
- expect(rendered).to.equal(expected);
114
- });
115
-
116
- it('should self-close void elements', () => {
117
- let rendered = render(<div><input type='text' /><wbr /></div>),
118
- expected = `<div><input type="text" /><wbr /></div>`;
119
-
120
- expect(rendered).to.equal(expected);
121
- });
122
-
123
- it('does not close void elements with closing tags', () => {
124
- let rendered = render(<input><p>Hello World</p></input>),
125
- expected = `<input /><p>Hello World</p>`;
126
-
127
- expect(rendered).to.equal(expected);
128
- });
129
-
130
- it('should serialize object styles', () => {
131
- let rendered = render(<div style={{ color:'red', border:'none' }} />),
132
- expected = `<div style="color: red; border: none;"></div>`;
133
-
134
- expect(rendered).to.equal(expected);
135
- });
136
-
137
- it('should ignore empty object styles', () => {
138
- let rendered = render(<div style={{}} />),
139
- expected = `<div></div>`;
140
-
141
- expect(rendered).to.equal(expected);
142
- });
143
-
144
- it('should render SVG elements', () => {
145
- let rendered = render((
146
- <svg>
147
- <image xlinkHref="#" />
148
- <foreignObject>
149
- <div xlinkHref="#" />
150
- </foreignObject>
151
- <g>
152
- <image xlinkHref="#" />
153
- </g>
154
- </svg>
155
- ));
156
-
157
- expect(rendered).to.equal(`<svg><image xlink:href="#"></image><foreignObject><div xlinkHref="#"></div></foreignObject><g><image xlink:href="#"></image></g></svg>`);
158
- });
159
- });
160
-
161
- describe('Functional Components', () => {
162
- it('should render functional components', () => {
163
- let Test = spy( ({ foo, children }) => <div foo={foo}>{ children }</div> );
164
-
165
- let rendered = render(<Test foo="test">content</Test>);
166
-
167
- expect(rendered)
168
- .to.equal(`<div foo="test">content</div>`);
169
-
170
- expect(Test)
171
- .to.have.been.calledOnce
172
- .and.calledWithExactly(
173
- match({
174
- foo: 'test',
175
- children: ['content']
176
- }),
177
- match({})
178
- );
179
- });
180
-
181
- it('should render functional components within JSX', () => {
182
- let Test = spy( ({ foo, children }) => <div foo={foo}>{ children }</div> );
183
-
184
- let rendered = render(
185
- <section>
186
- <Test foo={1}><span>asdf</span></Test>
187
- </section>
188
- );
189
-
190
- expect(rendered)
191
- .to.equal(`<section><div foo="1"><span>asdf</span></div></section>`);
192
-
193
- expect(Test)
194
- .to.have.been.calledOnce
195
- .and.calledWithExactly(
196
- match({
197
- foo: 1,
198
- children: [
199
- match({ nodeName:'span', children:['asdf'] })
200
- ]
201
- }),
202
- match({})
203
- );
204
- });
205
-
206
- it('should apply defaultProps', () => {
207
- const Test = props => <div {...props} />;
208
- Test.defaultProps = {
209
- foo: 'default foo',
210
- bar: 'default bar'
211
- };
212
-
213
- expect(render(<Test />), 'defaults').to.equal('<div foo="default foo" bar="default bar"></div>');
214
- expect(render(<Test bar="b" />), 'partial').to.equal('<div foo="default foo" bar="b"></div>');
215
- expect(render(<Test foo="a" bar="b" />), 'overridden').to.equal('<div foo="a" bar="b"></div>');
216
- });
217
- });
218
-
219
- describe('Classical Components', () => {
220
- it('should render classical components', () => {
221
- let Test = spy(class Test extends Component {
222
- render({ foo, children }, state) {
223
- return <div foo={foo}>{ children }</div>;
224
- }
225
- });
226
- spy(Test.prototype, 'render');
227
-
228
- let rendered = render(<Test foo="test">content</Test>);
229
-
230
- const PROPS = {
231
- foo: 'test',
232
- children: ['content']
233
- };
234
-
235
- expect(rendered)
236
- .to.equal(`<div foo="test">content</div>`);
237
-
238
- expect(Test)
239
- .to.have.been.calledOnce
240
- .and.calledWith(match(PROPS), match({}));
241
-
242
- expect(Test.prototype.render)
243
- .to.have.been.calledOnce
244
- .and.calledWithExactly(
245
- match(PROPS),
246
- match({}), // empty state
247
- match({}) // empty context
248
- );
249
- });
250
-
251
- it('should render classical components within JSX', () => {
252
- let Test = spy(class Test extends Component {
253
- render({ foo, children }, state) {
254
- return <div foo={foo}>{ children }</div>;
255
- }
256
- });
257
-
258
- spy(Test.prototype, 'render');
259
-
260
- let rendered = render(
261
- <section>
262
- <Test foo={1}><span>asdf</span></Test>
263
- </section>
264
- );
265
-
266
- expect(rendered)
267
- .to.equal(`<section><div foo="1"><span>asdf</span></div></section>`);
268
-
269
- expect(Test).to.have.been.calledOnce;
270
-
271
- expect(Test.prototype.render)
272
- .to.have.been.calledOnce
273
- .and.calledWithExactly(
274
- match({
275
- foo: 1,
276
- children: [
277
- match({ nodeName:'span', children:['asdf'] })
278
- ]
279
- }),
280
- match({}), // empty state
281
- match({})
282
- );
283
- });
284
-
285
- it('should apply defaultProps', () => {
286
- class Test extends Component {
287
- static defaultProps = {
288
- foo: 'default foo',
289
- bar: 'default bar'
290
- };
291
- render(props) {
292
- return <div {...props} />;
293
- }
294
- }
295
-
296
- expect(render(<Test />), 'defaults').to.equal('<div foo="default foo" bar="default bar"></div>');
297
- expect(render(<Test bar="b" />), 'partial').to.equal('<div foo="default foo" bar="b"></div>');
298
- expect(render(<Test foo="a" bar="b" />), 'overridden').to.equal('<div foo="a" bar="b"></div>');
299
- });
300
-
301
- it('should invoke componentWillMount', () => {
302
- class Test extends Component {
303
- componentWillMount() {}
304
- render(props) {
305
- return <div {...props} />;
306
- }
307
- }
308
- spy(Test.prototype, 'componentWillMount');
309
- spy(Test.prototype, 'render');
310
-
311
- render(<Test />);
312
-
313
- expect(Test.prototype.componentWillMount)
314
- .to.have.been.calledOnce
315
- .and.to.have.been.calledBefore(Test.prototype.render);
316
- });
317
-
318
- it('should pass context to grandchildren', () => {
319
- const CONTEXT = { a:'a' };
320
- const PROPS = { b:'b' };
321
-
322
- class Outer extends Component {
323
- getChildContext() {
324
- return CONTEXT;
325
- }
326
- render(props) {
327
- return <div><Inner {...props} /></div>;
328
- }
329
- }
330
- spy(Outer.prototype, 'getChildContext');
331
-
332
- class Inner extends Component {
333
- render(props, state, context) {
334
- return <div>{ context && context.a }</div>;
335
- }
336
- }
337
- spy(Inner.prototype, 'render');
338
-
339
- render(<Outer />);
340
-
341
- expect(Outer.prototype.getChildContext).to.have.been.calledOnce;
342
- expect(Inner.prototype.render).to.have.been.calledWith(match({}), {}, CONTEXT);
343
-
344
- CONTEXT.foo = 'bar';
345
- render(<Outer {...PROPS} />);
346
-
347
- expect(Outer.prototype.getChildContext).to.have.been.calledTwice;
348
- expect(Inner.prototype.render).to.have.been.calledWith(match(PROPS), {}, CONTEXT);
349
- });
350
-
351
- it('should pass context to direct children', () => {
352
- const CONTEXT = { a:'a' };
353
- const PROPS = { b:'b' };
354
-
355
- class Outer extends Component {
356
- getChildContext() {
357
- return CONTEXT;
358
- }
359
- render(props) {
360
- return <Inner {...props} />;
361
- }
362
- }
363
- spy(Outer.prototype, 'getChildContext');
364
-
365
- class Inner extends Component {
366
- render(props, state, context) {
367
- return <div>{ context && context.a }</div>;
368
- }
369
- }
370
- spy(Inner.prototype, 'render');
371
-
372
- render(<Outer />);
373
-
374
- expect(Outer.prototype.getChildContext).to.have.been.calledOnce;
375
- expect(Inner.prototype.render).to.have.been.calledWith(match({}), {}, CONTEXT);
376
-
377
- CONTEXT.foo = 'bar';
378
- render(<Outer {...PROPS} />);
379
-
380
- expect(Outer.prototype.getChildContext).to.have.been.calledTwice;
381
- expect(Inner.prototype.render).to.have.been.calledWith(match(PROPS), {}, CONTEXT);
382
-
383
- // make sure render() could make use of context.a
384
- expect(Inner.prototype.render).to.have.returned(match({ children:['a'] }));
385
- });
386
-
387
- it('should preserve existing context properties when creating child contexts', () => {
388
- let outerContext = { outer:true },
389
- innerContext = { inner:true };
390
- class Outer extends Component {
391
- getChildContext() {
392
- return { outerContext };
393
- }
394
- render() {
395
- return <div><Inner /></div>;
396
- }
397
- }
398
-
399
- class Inner extends Component {
400
- getChildContext() {
401
- return { innerContext };
402
- }
403
- render() {
404
- return <InnerMost />;
405
- }
406
- }
407
-
408
- class InnerMost extends Component {
409
- render() {
410
- return <strong>test</strong>;
411
- }
412
- }
413
-
414
- spy(Inner.prototype, 'render');
415
- spy(InnerMost.prototype, 'render');
416
-
417
- render(<Outer />);
418
-
419
- expect(Inner.prototype.render).to.have.been.calledWith(match({}), {}, { outerContext });
420
- expect(InnerMost.prototype.render).to.have.been.calledWith(match({}), {}, { outerContext, innerContext });
421
- });
422
- });
423
-
424
- describe('High-order components', () => {
425
- class Outer extends Component {
426
- render({ children, ...props }) {
427
- return <Inner {...props} a="b">child <span>{ children }</span></Inner>;
428
- }
429
- }
430
-
431
- class Inner extends Component {
432
- render({ children, ...props }) {
433
- return <div id="inner" {...props} b="c" c="d">{ children }</div>;
434
- }
435
- }
436
-
437
- it('should resolve+render high order components', () => {
438
- let rendered = render(<Outer a="a" b="b" p={1}>foo</Outer>);
439
- expect(rendered).to.equal('<div id="inner" a="b" b="c" p="1" c="d">child <span>foo</span></div>');
440
- });
441
-
442
- it('should render child inline when shallow=true', () => {
443
- let rendered = shallowRender(<Outer a="a" b="b" p={1}>foo</Outer>);
444
- expect(rendered).to.equal('<Inner a="b" b="b" p="1">child <span>foo</span></Inner>');
445
- });
446
-
447
- it('should render nested high order components when shallowHighOrder=false', () => {
448
- // using functions for meaningful generation of displayName
449
- function Outer() { return <Middle />; }
450
- function Middle() { return <div><Inner /></div>; }
451
- function Inner() { return 'hi'; }
452
-
453
- let rendered = render(<Outer />);
454
- expect(rendered).to.equal('<div>hi</div>');
455
-
456
- rendered = render(<Outer />, null, { shallow:true });
457
- expect(rendered, '{shallow:true}').to.equal('<Middle></Middle>');
458
-
459
- rendered = render(<Outer />, null, { shallow:true, shallowHighOrder:false });
460
- expect(rendered, '{shallow:true,shallowHighOrder:false}').to.equal('<div><Inner></Inner></div>', 'but it should never render nested grandchild components');
461
- });
462
- });
463
-
464
- describe('dangerouslySetInnerHTML', () => {
465
- it('should support dangerouslySetInnerHTML', () => {
466
- // some invalid HTML to make sure we're being flakey:
467
- let html = '<a href="foo">asdf</a> some text <ul><li>foo<li>bar</ul>';
468
- let rendered = render(<div id="f" dangerouslySetInnerHTML={{__html:html}} />);
469
- expect(rendered).to.equal(`<div id="f">${html}</div>`);
470
- });
471
-
472
- it('should override children', () => {
473
- let rendered = render(<div dangerouslySetInnerHTML={{__html:'foo'}}><b>bar</b></div>);
474
- expect(rendered).to.equal('<div>foo</div>');
475
- });
476
- });
477
-
478
- describe('className / class massaging', () => {
479
- it('should render class using className', () => {
480
- let rendered = render(<div className="foo bar" />);
481
- expect(rendered).to.equal('<div class="foo bar"></div>');
482
- });
483
-
484
- it('should render class using class', () => {
485
- let rendered = render(<div class="foo bar" />);
486
- expect(rendered).to.equal('<div class="foo bar"></div>');
487
- });
488
-
489
- it('should prefer class over className', () => {
490
- let rendered = render(<div class="foo" className="foo bar" />);
491
- expect(rendered).to.equal('<div class="foo"></div>');
492
- });
493
-
494
- it('should stringify object classNames', () => {
495
- let rendered = render(<div class={{ foo:1, bar:0, baz:true, buzz:false }} />);
496
- expect(rendered, 'class').to.equal('<div class="foo baz"></div>');
497
-
498
- rendered = render(<div className={{ foo:1, bar:0, baz:true, buzz:false }} />);
499
- expect(rendered, 'className').to.equal('<div class="foo baz"></div>');
500
- });
501
- });
502
-
503
- describe('sortAttributes', () => {
504
- it('should leave attributes unsorted by default', () => {
505
- let rendered = render(<div b1="b1" c="c" a="a" b="b" />);
506
- expect(rendered).to.equal('<div b1="b1" c="c" a="a" b="b"></div>');
507
- });
508
-
509
- it('should sort attributes lexicographically if enabled', () => {
510
- let rendered = render(<div b1="b1" c="c" a="a" b="b" />, null, { sortAttributes:true });
511
- expect(rendered).to.equal('<div a="a" b="b" b1="b1" c="c"></div>');
512
- });
513
- });
514
-
515
- describe('xml:true', () => {
516
- let renderXml = jsx => render(jsx, null, { xml:true });
517
-
518
- it('should render end-tags', () => {
519
- expect(renderXml(<div />)).to.equal(`<div />`);
520
- expect(renderXml(<a />)).to.equal(`<a />`);
521
- expect(renderXml(<a>b</a>)).to.equal(`<a>b</a>`);
522
- });
523
-
524
- it('should render boolean attributes with named values', () => {
525
- expect(renderXml(<div foo bar />)).to.equal(`<div foo="foo" bar="bar" />`);
526
- });
527
-
528
- it('should exclude falsey attributes', () => {
529
- expect(renderXml(<div foo={false} bar={0} />)).to.equal(`<div bar="0" />`);
530
- });
531
- });
532
-
533
- describe('state locking', () => {
534
- it('should set _disable and __x to true', () => {
535
- let inst;
536
- class Foo extends Component {
537
- constructor(props, context) {
538
- super(props, context);
539
- inst = this;
540
- }
541
- render() {
542
- return <div />;
543
- }
544
- }
545
-
546
- expect(render(<Foo />)).to.equal('<div></div>');
547
-
548
- expect(inst).to.have.property('_disable', true);
549
- expect(inst).to.have.property('__x', true);
550
- });
551
-
552
- it('should prevent re-rendering', () => {
553
- const Bar = stub().returns(<div />);
554
-
555
- let count = 0;
556
-
557
- class Foo extends Component {
558
- componentWillMount() {
559
- this.forceUpdate();
560
- }
561
- render() {
562
- return <Bar count={++count} />;
563
- }
564
- }
565
-
566
- expect(render(<Foo />)).to.equal('<div></div>');
567
-
568
- expect(Bar).to.have.been.calledOnce.and.calledWithMatch({ count: 1 });
569
- });
570
- });
571
- });
@@ -1,36 +0,0 @@
1
- import { shallowRender } from '../src';
2
- import { h, Component } from 'preact';
3
- import chai, { expect } from 'chai';
4
- import { spy, match } from 'sinon';
5
- import sinonChai from 'sinon-chai';
6
- chai.use(sinonChai);
7
-
8
- describe('shallowRender()', () => {
9
- it('should not render nested components', () => {
10
- let Test = spy( ({ foo, children }) => <div bar={foo}><b>test child</b>{ children }</div> );
11
- Test.displayName = 'Test';
12
-
13
- let rendered = shallowRender(
14
- <section>
15
- <Test foo={1}><span>asdf</span></Test>
16
- </section>
17
- );
18
-
19
- expect(rendered).to.equal(`<section><Test foo="1"><span>asdf</span></Test></section>`);
20
- expect(Test).not.to.have.been.called;
21
- });
22
-
23
- it('should always render root component', () => {
24
- let Test = spy( ({ foo, children }) => <div bar={foo}><b>test child</b>{ children }</div> );
25
- Test.displayName = 'Test';
26
-
27
- let rendered = shallowRender(
28
- <Test foo={1}>
29
- <span>asdf</span>
30
- </Test>
31
- );
32
-
33
- expect(rendered).to.equal(`<div bar="1"><b>test child</b><span>asdf</span></div>`);
34
- expect(Test).to.have.been.calledOnce;
35
- });
36
- });