ripple 0.2.20 → 0.2.21
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/package.json
CHANGED
|
@@ -77,20 +77,6 @@ function RipplePlugin(config) {
|
|
|
77
77
|
return this.finishNode(node, 'JSXExpressionContainer');
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
jsx_parseBindingExpression() {
|
|
81
|
-
let node = this.startNode();
|
|
82
|
-
this.next();
|
|
83
|
-
|
|
84
|
-
if (this.type === tt.bracketR) {
|
|
85
|
-
this.raise(node.start, 'bindings must only be assigned a non-empty expression');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
node.expression = this.parseExpression();
|
|
89
|
-
|
|
90
|
-
this.expect(tt.bracketR);
|
|
91
|
-
return this.finishNode(node, 'BindingExpression');
|
|
92
|
-
}
|
|
93
|
-
|
|
94
80
|
jsx_parseTupleContainer() {
|
|
95
81
|
var t = this.startNode();
|
|
96
82
|
return (
|
|
@@ -104,7 +90,6 @@ function RipplePlugin(config) {
|
|
|
104
90
|
|
|
105
91
|
jsx_parseAttribute() {
|
|
106
92
|
let node = this.startNode();
|
|
107
|
-
|
|
108
93
|
if (this.eat(tt.braceL)) {
|
|
109
94
|
if (this.type.label === '@') {
|
|
110
95
|
this.next();
|
|
@@ -144,9 +129,6 @@ function RipplePlugin(config) {
|
|
|
144
129
|
const tok = this.acornTypeScript.tokTypes;
|
|
145
130
|
|
|
146
131
|
switch (this.type) {
|
|
147
|
-
case tt.bracketL:
|
|
148
|
-
var t = this.jsx_parseBindingExpression();
|
|
149
|
-
return t;
|
|
150
132
|
case tt.braceL:
|
|
151
133
|
var t = this.jsx_parseExpressionContainer();
|
|
152
134
|
return (
|
|
@@ -196,21 +178,6 @@ function RipplePlugin(config) {
|
|
|
196
178
|
}
|
|
197
179
|
return this.finishNode(node, 'TryStatement');
|
|
198
180
|
}
|
|
199
|
-
|
|
200
|
-
// updateContext(e) {
|
|
201
|
-
// const s = this.type;
|
|
202
|
-
// const context = this.curContext();
|
|
203
|
-
// const tokContexts = this.acornTypeScript.tokContexts;
|
|
204
|
-
|
|
205
|
-
// if (s == tt.bracketL && context == tokContexts.tc_oTag) {
|
|
206
|
-
// this.context.push(tc.b_expr);
|
|
207
|
-
// } else if (s == tt.bracketR && context == tc.b_expr) {
|
|
208
|
-
// this.context.pop();
|
|
209
|
-
// }
|
|
210
|
-
|
|
211
|
-
// return super.updateContext(e);
|
|
212
|
-
// }
|
|
213
|
-
|
|
214
181
|
jsx_readToken() {
|
|
215
182
|
let out = '',
|
|
216
183
|
chunkStart = this.pos;
|
package/src/compiler/utils.js
CHANGED
|
@@ -466,7 +466,13 @@ export function visit_assignment_expression(node, context, build_assignment) {
|
|
|
466
466
|
throw new Error(`Unexpected assignment type ${node.left.type}`);
|
|
467
467
|
}
|
|
468
468
|
|
|
469
|
-
|
|
469
|
+
const transformed = build_assignment(node.operator, node.left, node.right, context);
|
|
470
|
+
|
|
471
|
+
if (transformed === node.left) {
|
|
472
|
+
return node;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return transformed;
|
|
470
476
|
}
|
|
471
477
|
|
|
472
478
|
export function build_assignment(operator, left, right, context) {
|
package/tests/basic.test.ripple
CHANGED
|
@@ -275,6 +275,25 @@ describe('basic', () => {
|
|
|
275
275
|
expect(div.getAttribute('data-extra')).toBe('value');
|
|
276
276
|
});
|
|
277
277
|
|
|
278
|
+
it('renders without crashing', () => {
|
|
279
|
+
component App() {
|
|
280
|
+
let foo;
|
|
281
|
+
let bar;
|
|
282
|
+
let baz;
|
|
283
|
+
|
|
284
|
+
foo = {};
|
|
285
|
+
foo = {'test': 0};
|
|
286
|
+
foo['abc'] = 123;
|
|
287
|
+
|
|
288
|
+
bar = { 'def': 456 };
|
|
289
|
+
|
|
290
|
+
baz = { 'ghi': 789 };
|
|
291
|
+
baz['jkl'] = 987;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
render(App);
|
|
295
|
+
});
|
|
296
|
+
|
|
278
297
|
it('renders multiple reactive lexical blocks', () => {
|
|
279
298
|
component Basic() {
|
|
280
299
|
<div>
|
|
@@ -344,4 +363,458 @@ describe('basic', () => {
|
|
|
344
363
|
|
|
345
364
|
expect(container.querySelector('.count').textContent).toBe('0');
|
|
346
365
|
});
|
|
366
|
+
|
|
367
|
+
it('renders with different event types', () => {
|
|
368
|
+
component Basic() {
|
|
369
|
+
let $focusCount = 0;
|
|
370
|
+
let $clickCount = 0;
|
|
371
|
+
|
|
372
|
+
<button
|
|
373
|
+
onFocus={() => $focusCount++}
|
|
374
|
+
onClick={() => $clickCount++}
|
|
375
|
+
>{'Test Button'}</button>
|
|
376
|
+
<div class='focus-count'>{$focusCount}</div>
|
|
377
|
+
<div class='click-count'>{$clickCount}</div>
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
render(Basic);
|
|
381
|
+
|
|
382
|
+
const button = container.querySelector('button');
|
|
383
|
+
const focusDiv = container.querySelector('.focus-count');
|
|
384
|
+
const clickDiv = container.querySelector('.click-count');
|
|
385
|
+
|
|
386
|
+
button.dispatchEvent(new Event('focus'));
|
|
387
|
+
flushSync();
|
|
388
|
+
expect(focusDiv.textContent).toBe('1');
|
|
389
|
+
|
|
390
|
+
button.click();
|
|
391
|
+
flushSync();
|
|
392
|
+
expect(clickDiv.textContent).toBe('1');
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it('renders with capture events', () => {
|
|
396
|
+
component Basic() {
|
|
397
|
+
let $captureClicks = 0;
|
|
398
|
+
let $bubbleClicks = 0;
|
|
399
|
+
|
|
400
|
+
<div onClickCapture={() => $captureClicks++}>
|
|
401
|
+
<button onClick={() => $bubbleClicks++}>{'Click me'}</button>
|
|
402
|
+
<div class='capture-count'>{$captureClicks}</div>
|
|
403
|
+
<div class='bubble-count'>{$bubbleClicks}</div>
|
|
404
|
+
</div>
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
render(Basic);
|
|
408
|
+
|
|
409
|
+
const button = container.querySelector('button');
|
|
410
|
+
const captureDiv = container.querySelector('.capture-count');
|
|
411
|
+
const bubbleDiv = container.querySelector('.bubble-count');
|
|
412
|
+
|
|
413
|
+
button.click();
|
|
414
|
+
flushSync();
|
|
415
|
+
|
|
416
|
+
expect(captureDiv.textContent).toBe('1');
|
|
417
|
+
expect(bubbleDiv.textContent).toBe('1');
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it('renders with component composition and children', () => {
|
|
421
|
+
component Card(props) {
|
|
422
|
+
<div class='card'>
|
|
423
|
+
<props.$children />
|
|
424
|
+
</div>
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
component Basic() {
|
|
428
|
+
<Card>
|
|
429
|
+
component $children() {
|
|
430
|
+
<p>{'Card content here'}</p>
|
|
431
|
+
}
|
|
432
|
+
</Card>
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
render(Basic);
|
|
436
|
+
|
|
437
|
+
const card = container.querySelector('.card');
|
|
438
|
+
const paragraph = card.querySelector('p');
|
|
439
|
+
|
|
440
|
+
expect(card).toBeTruthy();
|
|
441
|
+
expect(paragraph.textContent).toBe('Card content here');
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it('renders with error handling simulation', () => {
|
|
445
|
+
component Basic() {
|
|
446
|
+
let $hasError = false;
|
|
447
|
+
let $errorMessage = '';
|
|
448
|
+
|
|
449
|
+
const triggerError = () => {
|
|
450
|
+
try {
|
|
451
|
+
throw new Error('Test error');
|
|
452
|
+
} catch (e) {
|
|
453
|
+
$hasError = true;
|
|
454
|
+
$errorMessage = e.message;
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
<div>
|
|
459
|
+
<button onClick={triggerError}>{'Trigger Error'}</button>
|
|
460
|
+
if ($hasError) {
|
|
461
|
+
<div class='error'>{'Error caught: ' + $errorMessage}</div>
|
|
462
|
+
} else {
|
|
463
|
+
<div class='success'>{'No error'}</div>
|
|
464
|
+
}
|
|
465
|
+
</div>
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
render(Basic);
|
|
469
|
+
|
|
470
|
+
const button = container.querySelector('button');
|
|
471
|
+
const successDiv = container.querySelector('.success');
|
|
472
|
+
|
|
473
|
+
expect(successDiv).toBeTruthy();
|
|
474
|
+
expect(successDiv.textContent).toBe('No error');
|
|
475
|
+
|
|
476
|
+
button.click();
|
|
477
|
+
flushSync();
|
|
478
|
+
|
|
479
|
+
const errorDiv = container.querySelector('.error');
|
|
480
|
+
expect(errorDiv).toBeTruthy();
|
|
481
|
+
expect(errorDiv.textContent).toBe('Error caught: Test error');
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
it('renders with computed reactive state', () => {
|
|
485
|
+
component Basic() {
|
|
486
|
+
let $count = 5;
|
|
487
|
+
|
|
488
|
+
<div class='count'>{$count}</div>
|
|
489
|
+
<div class='doubled'>{$count * 2}</div>
|
|
490
|
+
<div class='is-even'>{$count % 2 === 0 ? 'Even' : 'Odd'}</div>
|
|
491
|
+
<button onClick={() => $count++}>{'Increment'}</button>
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
render(Basic);
|
|
495
|
+
|
|
496
|
+
const countDiv = container.querySelector('.count');
|
|
497
|
+
const doubledDiv = container.querySelector('.doubled');
|
|
498
|
+
const evenDiv = container.querySelector('.is-even');
|
|
499
|
+
const button = container.querySelector('button');
|
|
500
|
+
|
|
501
|
+
expect(countDiv.textContent).toBe('5');
|
|
502
|
+
expect(doubledDiv.textContent).toBe('10');
|
|
503
|
+
expect(evenDiv.textContent).toBe('Odd');
|
|
504
|
+
|
|
505
|
+
button.click();
|
|
506
|
+
flushSync();
|
|
507
|
+
|
|
508
|
+
expect(countDiv.textContent).toBe('6');
|
|
509
|
+
expect(doubledDiv.textContent).toBe('12');
|
|
510
|
+
expect(evenDiv.textContent).toBe('Even');
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it('renders with simple reactive objects', () => {
|
|
514
|
+
component Basic() {
|
|
515
|
+
let $user = {
|
|
516
|
+
$name: 'John',
|
|
517
|
+
$age: 25
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
<div class='name'>{$user.$name}</div>
|
|
521
|
+
<div class='age'>{$user.$age}</div>
|
|
522
|
+
<button onClick={() => {
|
|
523
|
+
$user.$name = 'Jane';
|
|
524
|
+
$user.$age = 30;
|
|
525
|
+
}}>{'Update User'}</button>
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
render(Basic);
|
|
529
|
+
|
|
530
|
+
const nameDiv = container.querySelector('.name');
|
|
531
|
+
const ageDiv = container.querySelector('.age');
|
|
532
|
+
const button = container.querySelector('button');
|
|
533
|
+
|
|
534
|
+
expect(nameDiv.textContent).toBe('John');
|
|
535
|
+
expect(ageDiv.textContent).toBe('25');
|
|
536
|
+
|
|
537
|
+
button.click();
|
|
538
|
+
flushSync();
|
|
539
|
+
|
|
540
|
+
expect(nameDiv.textContent).toBe('Jane');
|
|
541
|
+
expect(ageDiv.textContent).toBe('30');
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it('renders with conditional rendering using if statements', () => {
|
|
545
|
+
component Basic() {
|
|
546
|
+
let $showContent = false;
|
|
547
|
+
let $userRole = 'guest';
|
|
548
|
+
|
|
549
|
+
<button onClick={() => $showContent = !$showContent}>{'Toggle Content'}</button>
|
|
550
|
+
<button onClick={() => $userRole = $userRole === 'guest' ? 'admin' : 'guest'}>{'Toggle Role'}</button>
|
|
551
|
+
|
|
552
|
+
<div class='content'>
|
|
553
|
+
if ($showContent) {
|
|
554
|
+
if ($userRole === 'admin') {
|
|
555
|
+
<div class='admin-content'>{'Admin content'}</div>
|
|
556
|
+
} else {
|
|
557
|
+
<div class='user-content'>{'User content'}</div>
|
|
558
|
+
}
|
|
559
|
+
} else {
|
|
560
|
+
<div class='no-content'>{'No content'}</div>
|
|
561
|
+
}
|
|
562
|
+
</div>
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
render(Basic);
|
|
566
|
+
|
|
567
|
+
const buttons = container.querySelectorAll('button');
|
|
568
|
+
const contentDiv = container.querySelector('.content');
|
|
569
|
+
|
|
570
|
+
expect(contentDiv.querySelector('.no-content')).toBeTruthy();
|
|
571
|
+
expect(contentDiv.querySelector('.admin-content')).toBeFalsy();
|
|
572
|
+
expect(contentDiv.querySelector('.user-content')).toBeFalsy();
|
|
573
|
+
|
|
574
|
+
buttons[0].click();
|
|
575
|
+
flushSync();
|
|
576
|
+
|
|
577
|
+
expect(contentDiv.querySelector('.no-content')).toBeFalsy();
|
|
578
|
+
expect(contentDiv.querySelector('.user-content')).toBeTruthy();
|
|
579
|
+
expect(contentDiv.querySelector('.admin-content')).toBeFalsy();
|
|
580
|
+
|
|
581
|
+
buttons[1].click();
|
|
582
|
+
flushSync();
|
|
583
|
+
|
|
584
|
+
expect(contentDiv.querySelector('.no-content')).toBeFalsy();
|
|
585
|
+
expect(contentDiv.querySelector('.user-content')).toBeFalsy();
|
|
586
|
+
expect(contentDiv.querySelector('.admin-content')).toBeTruthy();
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
it('renders with nested components and prop passing', () => {
|
|
590
|
+
component Button(props) {
|
|
591
|
+
<button class={props.variant} onClick={props.onClick}>
|
|
592
|
+
{props.label}
|
|
593
|
+
</button>
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
component Card(props) {
|
|
597
|
+
<div class='card'>
|
|
598
|
+
<h3>{props.title}</h3>
|
|
599
|
+
<p>{props.content}</p>
|
|
600
|
+
<Button variant='primary' label={props.buttonText} onClick={props.onAction} />
|
|
601
|
+
</div>
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
component Basic() {
|
|
605
|
+
let $clicked = false;
|
|
606
|
+
|
|
607
|
+
<Card
|
|
608
|
+
title='Test Card'
|
|
609
|
+
content='This is a test card'
|
|
610
|
+
buttonText='Click me'
|
|
611
|
+
onAction={() => $clicked = true}
|
|
612
|
+
/>
|
|
613
|
+
<div class='status'>{$clicked ? 'Clicked' : 'Not clicked'}</div>
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
render(Basic);
|
|
617
|
+
|
|
618
|
+
const card = container.querySelector('.card');
|
|
619
|
+
const title = card.querySelector('h3');
|
|
620
|
+
const content = card.querySelector('p');
|
|
621
|
+
const button = card.querySelector('button');
|
|
622
|
+
const status = container.querySelector('.status');
|
|
623
|
+
|
|
624
|
+
expect(title.textContent).toBe('Test Card');
|
|
625
|
+
expect(content.textContent).toBe('This is a test card');
|
|
626
|
+
expect(button.textContent).toBe('Click me');
|
|
627
|
+
expect(button.className).toBe('primary');
|
|
628
|
+
expect(status.textContent).toBe('Not clicked');
|
|
629
|
+
|
|
630
|
+
button.click();
|
|
631
|
+
flushSync();
|
|
632
|
+
|
|
633
|
+
expect(status.textContent).toBe('Clicked');
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
it('renders with complex event handling and state updates', () => {
|
|
637
|
+
component Basic() {
|
|
638
|
+
let $counter = 0;
|
|
639
|
+
let $history = [];
|
|
640
|
+
let $isEven = true;
|
|
641
|
+
|
|
642
|
+
const handleIncrement = () => {
|
|
643
|
+
$counter++;
|
|
644
|
+
$history = [...$history, `Inc to ${$counter}`];
|
|
645
|
+
$isEven = $counter % 2 === 0;
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
const handleDecrement = () => {
|
|
649
|
+
$counter--;
|
|
650
|
+
$history = [...$history, `Dec to ${$counter}`];
|
|
651
|
+
$isEven = $counter % 2 === 0;
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
const handleReset = () => {
|
|
655
|
+
$counter = 0;
|
|
656
|
+
$history = [...$history, 'Reset'];
|
|
657
|
+
$isEven = true;
|
|
658
|
+
};
|
|
659
|
+
|
|
660
|
+
<div class='counter'>{$counter}</div>
|
|
661
|
+
<div class='parity'>{$isEven ? 'Even' : 'Odd'}</div>
|
|
662
|
+
<div class='history-count'>{$history.length}</div>
|
|
663
|
+
|
|
664
|
+
<button class='inc-btn' onClick={handleIncrement}>{'+'}</button>
|
|
665
|
+
<button class='dec-btn' onClick={handleDecrement}>{'-'}</button>
|
|
666
|
+
<button class='reset-btn' onClick={handleReset}>{'Reset'}</button>
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
render(Basic);
|
|
670
|
+
|
|
671
|
+
const counterDiv = container.querySelector('.counter');
|
|
672
|
+
const parityDiv = container.querySelector('.parity');
|
|
673
|
+
const historyDiv = container.querySelector('.history-count');
|
|
674
|
+
const incBtn = container.querySelector('.inc-btn');
|
|
675
|
+
const decBtn = container.querySelector('.dec-btn');
|
|
676
|
+
const resetBtn = container.querySelector('.reset-btn');
|
|
677
|
+
|
|
678
|
+
expect(counterDiv.textContent).toBe('0');
|
|
679
|
+
expect(parityDiv.textContent).toBe('Even');
|
|
680
|
+
expect(historyDiv.textContent).toBe('0');
|
|
681
|
+
|
|
682
|
+
incBtn.click();
|
|
683
|
+
flushSync();
|
|
684
|
+
|
|
685
|
+
expect(counterDiv.textContent).toBe('1');
|
|
686
|
+
expect(parityDiv.textContent).toBe('Odd');
|
|
687
|
+
expect(historyDiv.textContent).toBe('1');
|
|
688
|
+
|
|
689
|
+
incBtn.click();
|
|
690
|
+
flushSync();
|
|
691
|
+
|
|
692
|
+
expect(counterDiv.textContent).toBe('2');
|
|
693
|
+
expect(parityDiv.textContent).toBe('Even');
|
|
694
|
+
expect(historyDiv.textContent).toBe('2');
|
|
695
|
+
|
|
696
|
+
decBtn.click();
|
|
697
|
+
flushSync();
|
|
698
|
+
|
|
699
|
+
expect(counterDiv.textContent).toBe('1');
|
|
700
|
+
expect(parityDiv.textContent).toBe('Odd');
|
|
701
|
+
expect(historyDiv.textContent).toBe('3');
|
|
702
|
+
|
|
703
|
+
resetBtn.click();
|
|
704
|
+
flushSync();
|
|
705
|
+
|
|
706
|
+
expect(counterDiv.textContent).toBe('0');
|
|
707
|
+
expect(parityDiv.textContent).toBe('Even');
|
|
708
|
+
expect(historyDiv.textContent).toBe('4');
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
it('renders with reactive component props', () => {
|
|
712
|
+
component ChildComponent(props) {
|
|
713
|
+
<div class='child-content'>{props.$text}</div>
|
|
714
|
+
<div class='child-count'>{props.$count}</div>
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
component Basic() {
|
|
718
|
+
let $message = 'Hello';
|
|
719
|
+
let $number = 1;
|
|
720
|
+
|
|
721
|
+
<ChildComponent $text={$message} $count={$number} />
|
|
722
|
+
<button onClick={() => {
|
|
723
|
+
$message = $message === 'Hello' ? 'Goodbye' : 'Hello';
|
|
724
|
+
$number++;
|
|
725
|
+
}}>{'Update Props'}</button>
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
render(Basic);
|
|
729
|
+
|
|
730
|
+
const contentDiv = container.querySelector('.child-content');
|
|
731
|
+
const countDiv = container.querySelector('.child-count');
|
|
732
|
+
const button = container.querySelector('button');
|
|
733
|
+
|
|
734
|
+
expect(contentDiv.textContent).toBe('Hello');
|
|
735
|
+
expect(countDiv.textContent).toBe('1');
|
|
736
|
+
|
|
737
|
+
button.click();
|
|
738
|
+
flushSync();
|
|
739
|
+
|
|
740
|
+
expect(contentDiv.textContent).toBe('Goodbye');
|
|
741
|
+
expect(countDiv.textContent).toBe('2');
|
|
742
|
+
|
|
743
|
+
button.click();
|
|
744
|
+
flushSync();
|
|
745
|
+
|
|
746
|
+
expect(contentDiv.textContent).toBe('Hello');
|
|
747
|
+
expect(countDiv.textContent).toBe('3');
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
it('renders with styling scoped to component', () => {
|
|
751
|
+
component Basic() {
|
|
752
|
+
<div class='styled-container'>
|
|
753
|
+
<h1>{'Styled heading'}</h1>
|
|
754
|
+
<p class='text'>{'Styled paragraph'}</p>
|
|
755
|
+
</div>
|
|
756
|
+
|
|
757
|
+
<style>
|
|
758
|
+
.styled-container {
|
|
759
|
+
background-color: rgb(0, 0, 255);
|
|
760
|
+
padding: 16px;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
h1 {
|
|
764
|
+
color: rgb(255, 255, 255);
|
|
765
|
+
font-size: 32px;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
.text {
|
|
769
|
+
color: rgb(200, 200, 200);
|
|
770
|
+
font-size: 14px;
|
|
771
|
+
}
|
|
772
|
+
</style>
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
render(Basic);
|
|
776
|
+
|
|
777
|
+
const styledContainer = container.querySelector('.styled-container');
|
|
778
|
+
const heading = styledContainer.querySelector('h1');
|
|
779
|
+
const paragraph = styledContainer.querySelector('.text');
|
|
780
|
+
|
|
781
|
+
expect(styledContainer).toBeTruthy();
|
|
782
|
+
expect(heading.textContent).toBe('Styled heading');
|
|
783
|
+
expect(paragraph.textContent).toBe('Styled paragraph');
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
it('renders with mixed static and dynamic content', () => {
|
|
787
|
+
component Basic() {
|
|
788
|
+
let $name = 'World';
|
|
789
|
+
let $count = 0;
|
|
790
|
+
const staticMessage = 'Welcome to Ripple!';
|
|
791
|
+
|
|
792
|
+
<div class='mixed-content'>
|
|
793
|
+
<h1>{staticMessage}</h1>
|
|
794
|
+
<p class='greeting'>{'Hello, ' + $name + '!'}</p>
|
|
795
|
+
<p class='notifications'>{'You have ' + $count + ' notifications'}</p>
|
|
796
|
+
<button onClick={() => $count++}>{'Add Notification'}</button>
|
|
797
|
+
<button onClick={() => $name = $name === 'World' ? 'User' : 'World'}>{'Toggle Name'}</button>
|
|
798
|
+
</div>
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
render(Basic);
|
|
802
|
+
|
|
803
|
+
const heading = container.querySelector('h1');
|
|
804
|
+
const greetingP = container.querySelector('.greeting');
|
|
805
|
+
const notificationsP = container.querySelector('.notifications');
|
|
806
|
+
const buttons = container.querySelectorAll('button');
|
|
807
|
+
|
|
808
|
+
expect(heading.textContent).toBe('Welcome to Ripple!');
|
|
809
|
+
expect(greetingP.textContent).toBe('Hello, World!');
|
|
810
|
+
expect(notificationsP.textContent).toBe('You have 0 notifications');
|
|
811
|
+
|
|
812
|
+
buttons[0].click();
|
|
813
|
+
flushSync();
|
|
814
|
+
expect(notificationsP.textContent).toBe('You have 1 notifications');
|
|
815
|
+
|
|
816
|
+
buttons[1].click();
|
|
817
|
+
flushSync();
|
|
818
|
+
expect(greetingP.textContent).toBe('Hello, User!');
|
|
819
|
+
});
|
|
347
820
|
});
|