ripple 0.2.115 → 0.2.118
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 +16 -16
- package/src/compiler/index.js +20 -1
- package/src/compiler/phases/1-parse/index.js +79 -0
- package/src/compiler/phases/3-transform/client/index.js +54 -8
- package/src/compiler/phases/3-transform/segments.js +107 -60
- package/src/compiler/phases/3-transform/server/index.js +21 -11
- package/src/compiler/types/index.d.ts +16 -0
- package/src/runtime/index-client.js +19 -185
- package/src/runtime/index-server.js +24 -0
- package/src/runtime/internal/client/bindings.js +443 -0
- package/src/runtime/internal/client/index.js +4 -0
- package/src/runtime/internal/client/runtime.js +10 -0
- package/src/runtime/internal/client/utils.js +0 -8
- package/src/runtime/map.js +11 -1
- package/src/runtime/set.js +11 -1
- package/tests/client/__snapshots__/for.test.ripple.snap +80 -0
- package/tests/client/_etc.test.ripple +5 -0
- package/tests/client/array/array.copy-within.test.ripple +120 -0
- package/tests/client/array/array.derived.test.ripple +495 -0
- package/tests/client/array/array.iteration.test.ripple +115 -0
- package/tests/client/array/array.mutations.test.ripple +385 -0
- package/tests/client/array/array.static.test.ripple +237 -0
- package/tests/client/array/array.to-methods.test.ripple +93 -0
- package/tests/client/basic/__snapshots__/basic.attributes.test.ripple.snap +60 -0
- package/tests/client/basic/__snapshots__/basic.rendering.test.ripple.snap +106 -0
- package/tests/client/basic/__snapshots__/basic.text.test.ripple.snap +49 -0
- package/tests/client/basic/basic.attributes.test.ripple +474 -0
- package/tests/client/basic/basic.collections.test.ripple +94 -0
- package/tests/client/basic/basic.components.test.ripple +225 -0
- package/tests/client/basic/basic.errors.test.ripple +126 -0
- package/tests/client/basic/basic.events.test.ripple +222 -0
- package/tests/client/basic/basic.reactivity.test.ripple +476 -0
- package/tests/client/basic/basic.rendering.test.ripple +204 -0
- package/tests/client/basic/basic.styling.test.ripple +63 -0
- package/tests/client/basic/basic.utilities.test.ripple +25 -0
- package/tests/client/boundaries.test.ripple +2 -21
- package/tests/client/compiler/__snapshots__/compiler.assignments.test.ripple.snap +12 -0
- package/tests/client/compiler/__snapshots__/compiler.typescript.test.ripple.snap +22 -0
- package/tests/client/compiler/compiler.assignments.test.ripple +112 -0
- package/tests/client/compiler/compiler.attributes.test.ripple +95 -0
- package/tests/client/compiler/compiler.basic.test.ripple +203 -0
- package/tests/client/compiler/compiler.regex.test.ripple +87 -0
- package/tests/client/compiler/compiler.typescript.test.ripple +29 -0
- package/tests/client/{__snapshots__/composite.test.ripple.snap → composite/__snapshots__/composite.render.test.ripple.snap} +2 -2
- package/tests/client/composite/composite.dynamic-components.test.ripple +100 -0
- package/tests/client/composite/composite.generics.test.ripple +211 -0
- package/tests/client/composite/composite.props.test.ripple +106 -0
- package/tests/client/composite/composite.reactivity.test.ripple +184 -0
- package/tests/client/composite/composite.render.test.ripple +84 -0
- package/tests/client/computed-properties.test.ripple +2 -21
- package/tests/client/context.test.ripple +5 -22
- package/tests/client/date.test.ripple +1 -20
- package/tests/client/dynamic-elements.test.ripple +16 -24
- package/tests/client/for.test.ripple +4 -23
- package/tests/client/head.test.ripple +11 -23
- package/tests/client/html.test.ripple +1 -20
- package/tests/client/input-value.test.ripple +11 -31
- package/tests/client/map.test.ripple +82 -20
- package/tests/client/media-query.test.ripple +10 -23
- package/tests/client/object.test.ripple +5 -24
- package/tests/client/portal.test.ripple +2 -19
- package/tests/client/ref.test.ripple +8 -26
- package/tests/client/set.test.ripple +84 -22
- package/tests/client/svg.test.ripple +1 -22
- package/tests/client/switch.test.ripple +6 -25
- package/tests/client/tracked-expression.test.ripple +2 -21
- package/tests/client/typescript-generics.test.ripple +0 -21
- package/tests/client/url/url.derived.test.ripple +83 -0
- package/tests/client/url/url.parsing.test.ripple +165 -0
- package/tests/client/url/url.partial-removal.test.ripple +198 -0
- package/tests/client/url/url.reactivity.test.ripple +449 -0
- package/tests/client/url/url.serialization.test.ripple +50 -0
- package/tests/client/url-search-params/url-search-params.derived.test.ripple +84 -0
- package/tests/client/url-search-params/url-search-params.initialization.test.ripple +61 -0
- package/tests/client/url-search-params/url-search-params.iteration.test.ripple +153 -0
- package/tests/client/url-search-params/url-search-params.mutation.test.ripple +343 -0
- package/tests/client/url-search-params/url-search-params.retrieval.test.ripple +160 -0
- package/tests/client/url-search-params/url-search-params.serialization.test.ripple +53 -0
- package/tests/client/url-search-params/url-search-params.tracked-url.test.ripple +55 -0
- package/tests/client.d.ts +12 -0
- package/tests/server/if.test.ripple +66 -0
- package/tests/setup-client.js +28 -0
- package/tsconfig.json +4 -2
- package/types/index.d.ts +92 -46
- package/LICENSE +0 -21
- package/tests/client/__snapshots__/basic.test.ripple.snap +0 -117
- package/tests/client/__snapshots__/compiler.test.ripple.snap +0 -33
- package/tests/client/array.test.ripple +0 -1455
- package/tests/client/basic.test.ripple +0 -1892
- package/tests/client/compiler.test.ripple +0 -541
- package/tests/client/composite.test.ripple +0 -692
- package/tests/client/url-search-params.test.ripple +0 -912
- package/tests/client/url.test.ripple +0 -954
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { flushSync, TrackedURL } from 'ripple';
|
|
2
|
+
|
|
3
|
+
describe('TrackedURL > partials/removal', () => {
|
|
4
|
+
it('handles URL with no port specified', () => {
|
|
5
|
+
component URLTest() {
|
|
6
|
+
const url = new TrackedURL('https://example.com/path');
|
|
7
|
+
|
|
8
|
+
<pre>{url.port}</pre>
|
|
9
|
+
<pre>{url.host}</pre>
|
|
10
|
+
<button onClick={() => url.port = '8080'}>{'Add Port'}</button>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
render(URLTest);
|
|
14
|
+
|
|
15
|
+
const button = container.querySelector('button');
|
|
16
|
+
|
|
17
|
+
// Initial state - default ports are empty strings
|
|
18
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('');
|
|
19
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('example.com');
|
|
20
|
+
|
|
21
|
+
// Add port
|
|
22
|
+
button.click();
|
|
23
|
+
flushSync();
|
|
24
|
+
|
|
25
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('8080');
|
|
26
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('example.com:8080');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('handles URL with no search params', () => {
|
|
30
|
+
component URLTest() {
|
|
31
|
+
const url = new TrackedURL('https://example.com/path');
|
|
32
|
+
|
|
33
|
+
<pre>{url.search}</pre>
|
|
34
|
+
<pre>{url.searchParams.size}</pre>
|
|
35
|
+
<button onClick={() => url.searchParams.append('foo', 'bar')}>{'Add Param'}</button>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
render(URLTest);
|
|
39
|
+
|
|
40
|
+
const button = container.querySelector('button');
|
|
41
|
+
|
|
42
|
+
// Initial state
|
|
43
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('');
|
|
44
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('0');
|
|
45
|
+
|
|
46
|
+
// Add param
|
|
47
|
+
button.click();
|
|
48
|
+
flushSync();
|
|
49
|
+
|
|
50
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('?foo=bar');
|
|
51
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('1');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('handles URL with no hash', () => {
|
|
55
|
+
component URLTest() {
|
|
56
|
+
const url = new TrackedURL('https://example.com/path');
|
|
57
|
+
|
|
58
|
+
<pre>{url.hash}</pre>
|
|
59
|
+
<button onClick={() => url.hash = '#section'}>{'Add Hash'}</button>
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
render(URLTest);
|
|
63
|
+
|
|
64
|
+
const button = container.querySelector('button');
|
|
65
|
+
|
|
66
|
+
// Initial state
|
|
67
|
+
expect(container.querySelector('pre').textContent).toBe('');
|
|
68
|
+
|
|
69
|
+
// Add hash
|
|
70
|
+
button.click();
|
|
71
|
+
flushSync();
|
|
72
|
+
|
|
73
|
+
expect(container.querySelector('pre').textContent).toBe('#section');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('handles removing port by setting empty string', () => {
|
|
77
|
+
component URLTest() {
|
|
78
|
+
const url = new TrackedURL('https://example.com:8080/path');
|
|
79
|
+
|
|
80
|
+
<button onClick={() => url.port = ''}>{'Remove Port'}</button>
|
|
81
|
+
<pre>{url.href}</pre>
|
|
82
|
+
<pre>{url.port}</pre>
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
render(URLTest);
|
|
86
|
+
|
|
87
|
+
const button = container.querySelector('button');
|
|
88
|
+
|
|
89
|
+
// Initial state
|
|
90
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com:8080/path');
|
|
91
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('8080');
|
|
92
|
+
|
|
93
|
+
// Remove port
|
|
94
|
+
button.click();
|
|
95
|
+
flushSync();
|
|
96
|
+
|
|
97
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com/path');
|
|
98
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('handles removing hash by setting empty string', () => {
|
|
102
|
+
component URLTest() {
|
|
103
|
+
const url = new TrackedURL('https://example.com/path#section');
|
|
104
|
+
|
|
105
|
+
<button onClick={() => url.hash = ''}>{'Remove Hash'}</button>
|
|
106
|
+
<pre>{url.href}</pre>
|
|
107
|
+
<pre>{url.hash}</pre>
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
render(URLTest);
|
|
111
|
+
|
|
112
|
+
const button = container.querySelector('button');
|
|
113
|
+
|
|
114
|
+
// Initial state
|
|
115
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com/path#section');
|
|
116
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('#section');
|
|
117
|
+
|
|
118
|
+
// Remove hash
|
|
119
|
+
button.click();
|
|
120
|
+
flushSync();
|
|
121
|
+
|
|
122
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com/path');
|
|
123
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('handles removing search by setting empty string', () => {
|
|
127
|
+
component URLTest() {
|
|
128
|
+
const url = new TrackedURL('https://example.com/path?foo=bar');
|
|
129
|
+
|
|
130
|
+
<button onClick={() => url.search = ''}>{'Remove Search'}</button>
|
|
131
|
+
<pre>{url.href}</pre>
|
|
132
|
+
<pre>{url.search}</pre>
|
|
133
|
+
<pre>{url.searchParams.size}</pre>
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
render(URLTest);
|
|
137
|
+
|
|
138
|
+
const button = container.querySelector('button');
|
|
139
|
+
|
|
140
|
+
// Initial state
|
|
141
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com/path?foo=bar');
|
|
142
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('?foo=bar');
|
|
143
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('1');
|
|
144
|
+
|
|
145
|
+
// Remove search
|
|
146
|
+
button.click();
|
|
147
|
+
flushSync();
|
|
148
|
+
|
|
149
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com/path');
|
|
150
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('');
|
|
151
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('0');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('handles hash without leading # character', () => {
|
|
155
|
+
component URLTest() {
|
|
156
|
+
const url = new TrackedURL('https://example.com/path');
|
|
157
|
+
|
|
158
|
+
<button onClick={() => url.hash = 'section'}>{'Set Hash'}</button>
|
|
159
|
+
<pre>{url.hash}</pre>
|
|
160
|
+
<pre>{url.href}</pre>
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
render(URLTest);
|
|
164
|
+
|
|
165
|
+
const button = container.querySelector('button');
|
|
166
|
+
|
|
167
|
+
// Initial state
|
|
168
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('');
|
|
169
|
+
|
|
170
|
+
// Set hash
|
|
171
|
+
button.click();
|
|
172
|
+
flushSync();
|
|
173
|
+
|
|
174
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('#section');
|
|
175
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('https://example.com/path#section');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('handles search without leading ? character', () => {
|
|
179
|
+
component URLTest() {
|
|
180
|
+
const url = new TrackedURL('https://example.com/path');
|
|
181
|
+
|
|
182
|
+
<button onClick={() => url.search = 'foo=bar'}>{'Set Search'}</button>
|
|
183
|
+
<pre>{url.search}</pre>
|
|
184
|
+
<pre>{url.href}</pre>
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
render(URLTest);
|
|
188
|
+
|
|
189
|
+
const button = container.querySelector('button');
|
|
190
|
+
|
|
191
|
+
// Set search
|
|
192
|
+
button.click();
|
|
193
|
+
flushSync();
|
|
194
|
+
|
|
195
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('foo=bar');
|
|
196
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('https://example.com/path?foo=bar');
|
|
197
|
+
});
|
|
198
|
+
});
|
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
import { flushSync, TrackedURL } from 'ripple';
|
|
2
|
+
|
|
3
|
+
describe('TrackedURL > reactivity', () => {
|
|
4
|
+
it('handles protocol changes with reactivity', () => {
|
|
5
|
+
component URLTest() {
|
|
6
|
+
const url = new TrackedURL('https://example.com/path');
|
|
7
|
+
|
|
8
|
+
<button onClick={() => url.protocol = 'http:'}>{'Change Protocol'}</button>
|
|
9
|
+
<pre>{url.href}</pre>
|
|
10
|
+
<pre>{url.protocol}</pre>
|
|
11
|
+
<pre>{url.origin}</pre>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
render(URLTest);
|
|
15
|
+
|
|
16
|
+
const button = container.querySelector('button');
|
|
17
|
+
|
|
18
|
+
// Initial state
|
|
19
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com/path');
|
|
20
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('https:');
|
|
21
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('https://example.com');
|
|
22
|
+
|
|
23
|
+
// Change protocol
|
|
24
|
+
button.click();
|
|
25
|
+
flushSync();
|
|
26
|
+
|
|
27
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('http://example.com/path');
|
|
28
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('http:');
|
|
29
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('http://example.com');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('handles hostname changes with reactivity', () => {
|
|
33
|
+
component URLTest() {
|
|
34
|
+
const url = new TrackedURL('https://example.com/path');
|
|
35
|
+
|
|
36
|
+
<button onClick={() => url.hostname = 'newdomain.com'}>{'Change Hostname'}</button>
|
|
37
|
+
<pre>{url.href}</pre>
|
|
38
|
+
<pre>{url.hostname}</pre>
|
|
39
|
+
<pre>{url.host}</pre>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
render(URLTest);
|
|
43
|
+
|
|
44
|
+
const button = container.querySelector('button');
|
|
45
|
+
|
|
46
|
+
// Initial state
|
|
47
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com/path');
|
|
48
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('example.com');
|
|
49
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('example.com');
|
|
50
|
+
|
|
51
|
+
// Change hostname
|
|
52
|
+
button.click();
|
|
53
|
+
flushSync();
|
|
54
|
+
|
|
55
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://newdomain.com/path');
|
|
56
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('newdomain.com');
|
|
57
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('newdomain.com');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('handles port changes with reactivity', () => {
|
|
61
|
+
component URLTest() {
|
|
62
|
+
const url = new TrackedURL('https://example.com:8080/path');
|
|
63
|
+
|
|
64
|
+
<button onClick={() => url.port = '9090'}>{'Change Port'}</button>
|
|
65
|
+
<pre>{url.href}</pre>
|
|
66
|
+
<pre>{url.port}</pre>
|
|
67
|
+
<pre>{url.host}</pre>
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
render(URLTest);
|
|
71
|
+
|
|
72
|
+
const button = container.querySelector('button');
|
|
73
|
+
|
|
74
|
+
// Initial state
|
|
75
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com:8080/path');
|
|
76
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('8080');
|
|
77
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('example.com:8080');
|
|
78
|
+
|
|
79
|
+
// Change port
|
|
80
|
+
button.click();
|
|
81
|
+
flushSync();
|
|
82
|
+
|
|
83
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com:9090/path');
|
|
84
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('9090');
|
|
85
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('example.com:9090');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('handles host changes with reactivity', () => {
|
|
89
|
+
component URLTest() {
|
|
90
|
+
const url = new TrackedURL('https://example.com:8080/path');
|
|
91
|
+
|
|
92
|
+
<button onClick={() => url.host = 'newdomain.com:9090'}>{'Change Host'}</button>
|
|
93
|
+
<pre>{url.href}</pre>
|
|
94
|
+
<pre>{url.host}</pre>
|
|
95
|
+
<pre>{url.hostname}</pre>
|
|
96
|
+
<pre>{url.port}</pre>
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
render(URLTest);
|
|
100
|
+
|
|
101
|
+
const button = container.querySelector('button');
|
|
102
|
+
|
|
103
|
+
// Initial state
|
|
104
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com:8080/path');
|
|
105
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('example.com:8080');
|
|
106
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('example.com');
|
|
107
|
+
expect(container.querySelectorAll('pre')[3].textContent).toBe('8080');
|
|
108
|
+
|
|
109
|
+
// Change host
|
|
110
|
+
button.click();
|
|
111
|
+
flushSync();
|
|
112
|
+
|
|
113
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://newdomain.com:9090/path');
|
|
114
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('newdomain.com:9090');
|
|
115
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('newdomain.com');
|
|
116
|
+
expect(container.querySelectorAll('pre')[3].textContent).toBe('9090');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('handles pathname changes with reactivity', () => {
|
|
120
|
+
component URLTest() {
|
|
121
|
+
const url = new TrackedURL('https://example.com/old-path');
|
|
122
|
+
|
|
123
|
+
<button onClick={() => url.pathname = '/new-path'}>{'Change Pathname'}</button>
|
|
124
|
+
<pre>{url.href}</pre>
|
|
125
|
+
<pre>{url.pathname}</pre>
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
render(URLTest);
|
|
129
|
+
|
|
130
|
+
const button = container.querySelector('button');
|
|
131
|
+
|
|
132
|
+
// Initial state
|
|
133
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com/old-path');
|
|
134
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('/old-path');
|
|
135
|
+
|
|
136
|
+
// Change pathname
|
|
137
|
+
button.click();
|
|
138
|
+
flushSync();
|
|
139
|
+
|
|
140
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com/new-path');
|
|
141
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('/new-path');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('handles search changes with reactivity', () => {
|
|
145
|
+
component URLTest() {
|
|
146
|
+
const url = new TrackedURL('https://example.com/path?foo=bar');
|
|
147
|
+
|
|
148
|
+
<button onClick={() => url.search = '?baz=qux'}>{'Change Search'}</button>
|
|
149
|
+
<pre>{url.href}</pre>
|
|
150
|
+
<pre>{url.search}</pre>
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
render(URLTest);
|
|
154
|
+
|
|
155
|
+
const button = container.querySelector('button');
|
|
156
|
+
|
|
157
|
+
// Initial state
|
|
158
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com/path?foo=bar');
|
|
159
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('?foo=bar');
|
|
160
|
+
|
|
161
|
+
// Change search
|
|
162
|
+
button.click();
|
|
163
|
+
flushSync();
|
|
164
|
+
|
|
165
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com/path?baz=qux');
|
|
166
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('?baz=qux');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('handles hash changes with reactivity', () => {
|
|
170
|
+
component URLTest() {
|
|
171
|
+
const url = new TrackedURL('https://example.com/path#section1');
|
|
172
|
+
|
|
173
|
+
<button onClick={() => url.hash = '#section2'}>{'Change Hash'}</button>
|
|
174
|
+
<pre>{url.href}</pre>
|
|
175
|
+
<pre>{url.hash}</pre>
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
render(URLTest);
|
|
179
|
+
|
|
180
|
+
const button = container.querySelector('button');
|
|
181
|
+
|
|
182
|
+
// Initial state
|
|
183
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com/path#section1');
|
|
184
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('#section1');
|
|
185
|
+
|
|
186
|
+
// Change hash
|
|
187
|
+
button.click();
|
|
188
|
+
flushSync();
|
|
189
|
+
|
|
190
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com/path#section2');
|
|
191
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('#section2');
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('handles username changes with reactivity', () => {
|
|
195
|
+
component URLTest() {
|
|
196
|
+
const url = new TrackedURL('https://user:pass@example.com/path');
|
|
197
|
+
|
|
198
|
+
<button onClick={() => url.username = 'newuser'}>{'Change Username'}</button>
|
|
199
|
+
<pre>{url.href}</pre>
|
|
200
|
+
<pre>{url.username}</pre>
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
render(URLTest);
|
|
204
|
+
|
|
205
|
+
const button = container.querySelector('button');
|
|
206
|
+
|
|
207
|
+
// Initial state
|
|
208
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://user:pass@example.com/path');
|
|
209
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('user');
|
|
210
|
+
|
|
211
|
+
// Change username
|
|
212
|
+
button.click();
|
|
213
|
+
flushSync();
|
|
214
|
+
|
|
215
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://newuser:pass@example.com/path');
|
|
216
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('newuser');
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('handles password changes with reactivity', () => {
|
|
220
|
+
component URLTest() {
|
|
221
|
+
const url = new TrackedURL('https://user:pass@example.com/path');
|
|
222
|
+
|
|
223
|
+
<button onClick={() => url.password = 'newpass'}>{'Change Password'}</button>
|
|
224
|
+
<pre>{url.href}</pre>
|
|
225
|
+
<pre>{url.password}</pre>
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
render(URLTest);
|
|
229
|
+
|
|
230
|
+
const button = container.querySelector('button');
|
|
231
|
+
|
|
232
|
+
// Initial state
|
|
233
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://user:pass@example.com/path');
|
|
234
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('pass');
|
|
235
|
+
|
|
236
|
+
// Change password
|
|
237
|
+
button.click();
|
|
238
|
+
flushSync();
|
|
239
|
+
|
|
240
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://user:newpass@example.com/path');
|
|
241
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('newpass');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('handles href changes with reactivity', () => {
|
|
245
|
+
component URLTest() {
|
|
246
|
+
const url = new TrackedURL('https://example.com/path?foo=bar#section');
|
|
247
|
+
|
|
248
|
+
<button onClick={() => url.href = 'https://newdomain.com:9090/newpath?baz=qux#newsection'}>{'Change Href'}</button>
|
|
249
|
+
<pre>{url.href}</pre>
|
|
250
|
+
<pre>{url.protocol}</pre>
|
|
251
|
+
<pre>{url.hostname}</pre>
|
|
252
|
+
<pre>{url.port}</pre>
|
|
253
|
+
<pre>{url.pathname}</pre>
|
|
254
|
+
<pre>{url.search}</pre>
|
|
255
|
+
<pre>{url.hash}</pre>
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
render(URLTest);
|
|
259
|
+
|
|
260
|
+
const button = container.querySelector('button');
|
|
261
|
+
|
|
262
|
+
// Initial state
|
|
263
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com/path?foo=bar#section');
|
|
264
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('https:');
|
|
265
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('example.com');
|
|
266
|
+
expect(container.querySelectorAll('pre')[3].textContent).toBe('');
|
|
267
|
+
expect(container.querySelectorAll('pre')[4].textContent).toBe('/path');
|
|
268
|
+
expect(container.querySelectorAll('pre')[5].textContent).toBe('?foo=bar');
|
|
269
|
+
expect(container.querySelectorAll('pre')[6].textContent).toBe('#section');
|
|
270
|
+
|
|
271
|
+
// Change href
|
|
272
|
+
button.click();
|
|
273
|
+
flushSync();
|
|
274
|
+
|
|
275
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://newdomain.com:9090/newpath?baz=qux#newsection');
|
|
276
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('https:');
|
|
277
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('newdomain.com');
|
|
278
|
+
expect(container.querySelectorAll('pre')[3].textContent).toBe('9090');
|
|
279
|
+
expect(container.querySelectorAll('pre')[4].textContent).toBe('/newpath');
|
|
280
|
+
expect(container.querySelectorAll('pre')[5].textContent).toBe('?baz=qux');
|
|
281
|
+
expect(container.querySelectorAll('pre')[6].textContent).toBe('#newsection');
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('handles origin property reactivity', () => {
|
|
285
|
+
component URLTest() {
|
|
286
|
+
const url = new TrackedURL('https://example.com:8080/path');
|
|
287
|
+
|
|
288
|
+
<button onClick={() => url.protocol = 'http:'}>{'Change Protocol'}</button>
|
|
289
|
+
<button onClick={() => url.hostname = 'newdomain.com'}>{'Change Hostname'}</button>
|
|
290
|
+
<button onClick={() => url.port = '9090'}>{'Change Port'}</button>
|
|
291
|
+
<pre>{url.origin}</pre>
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
render(URLTest);
|
|
295
|
+
|
|
296
|
+
const buttons = container.querySelectorAll('button');
|
|
297
|
+
|
|
298
|
+
// Initial state
|
|
299
|
+
expect(container.querySelector('pre').textContent).toBe('https://example.com:8080');
|
|
300
|
+
|
|
301
|
+
// Change protocol
|
|
302
|
+
buttons[0].click();
|
|
303
|
+
flushSync();
|
|
304
|
+
expect(container.querySelector('pre').textContent).toBe('http://example.com:8080');
|
|
305
|
+
|
|
306
|
+
// Change hostname
|
|
307
|
+
buttons[1].click();
|
|
308
|
+
flushSync();
|
|
309
|
+
expect(container.querySelector('pre').textContent).toBe('http://newdomain.com:8080');
|
|
310
|
+
|
|
311
|
+
// Change port
|
|
312
|
+
buttons[2].click();
|
|
313
|
+
flushSync();
|
|
314
|
+
expect(container.querySelector('pre').textContent).toBe('http://newdomain.com:9090');
|
|
315
|
+
});
|
|
316
|
+
it('handles searchParams changes with reactivity', () => {
|
|
317
|
+
component URLTest() {
|
|
318
|
+
const url = new TrackedURL('https://example.com/path?foo=bar');
|
|
319
|
+
const params = url.searchParams;
|
|
320
|
+
|
|
321
|
+
<button onClick={() => params.set('foo', 'updated')}>{'Update Foo'}</button>
|
|
322
|
+
<button onClick={() => params.append('baz', 'qux')}>{'Add Baz'}</button>
|
|
323
|
+
<pre>{url.href}</pre>
|
|
324
|
+
<pre>{url.search}</pre>
|
|
325
|
+
<pre>{params.get('foo')}</pre>
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
render(URLTest);
|
|
329
|
+
|
|
330
|
+
const updateButton = container.querySelectorAll('button')[0];
|
|
331
|
+
const addButton = container.querySelectorAll('button')[1];
|
|
332
|
+
|
|
333
|
+
// Initial state
|
|
334
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com/path?foo=bar');
|
|
335
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('?foo=bar');
|
|
336
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('bar');
|
|
337
|
+
|
|
338
|
+
// Update param
|
|
339
|
+
updateButton.click();
|
|
340
|
+
flushSync();
|
|
341
|
+
|
|
342
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com/path?foo=updated');
|
|
343
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('?foo=updated');
|
|
344
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('updated');
|
|
345
|
+
|
|
346
|
+
// Add param
|
|
347
|
+
addButton.click();
|
|
348
|
+
flushSync();
|
|
349
|
+
|
|
350
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('https://example.com/path?foo=updated&baz=qux');
|
|
351
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('?foo=updated&baz=qux');
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('handles search property updates reflected in searchParams', () => {
|
|
355
|
+
component URLTest() {
|
|
356
|
+
const url = new TrackedURL('https://example.com/path?foo=bar');
|
|
357
|
+
const params = url.searchParams;
|
|
358
|
+
|
|
359
|
+
<button onClick={() => url.search = '?baz=qux&test=value'}>{'Change Search'}</button>
|
|
360
|
+
<pre>{url.search}</pre>
|
|
361
|
+
<pre>{params.get('foo')}</pre>
|
|
362
|
+
<pre>{params.get('baz')}</pre>
|
|
363
|
+
<pre>{params.get('test')}</pre>
|
|
364
|
+
<pre>{params.size}</pre>
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
render(URLTest);
|
|
368
|
+
|
|
369
|
+
const button = container.querySelector('button');
|
|
370
|
+
|
|
371
|
+
// Initial state
|
|
372
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('?foo=bar');
|
|
373
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('bar');
|
|
374
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('');
|
|
375
|
+
expect(container.querySelectorAll('pre')[3].textContent).toBe('');
|
|
376
|
+
expect(container.querySelectorAll('pre')[4].textContent).toBe('1');
|
|
377
|
+
|
|
378
|
+
// Change search
|
|
379
|
+
button.click();
|
|
380
|
+
flushSync();
|
|
381
|
+
|
|
382
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('?baz=qux&test=value');
|
|
383
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('');
|
|
384
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('qux');
|
|
385
|
+
expect(container.querySelectorAll('pre')[3].textContent).toBe('value');
|
|
386
|
+
expect(container.querySelectorAll('pre')[4].textContent).toBe('2');
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it('handles multiple URL property changes in sequence', () => {
|
|
390
|
+
component URLTest() {
|
|
391
|
+
const url = new TrackedURL('https://example.com/path');
|
|
392
|
+
|
|
393
|
+
<button onClick={() => {
|
|
394
|
+
url.protocol = 'http:';
|
|
395
|
+
url.hostname = 'newdomain.com';
|
|
396
|
+
url.port = '8080';
|
|
397
|
+
url.pathname = '/api';
|
|
398
|
+
url.search = '?key=value';
|
|
399
|
+
url.hash = '#section';
|
|
400
|
+
}}>{'Change All'}</button>
|
|
401
|
+
<pre>{url.href}</pre>
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
render(URLTest);
|
|
405
|
+
|
|
406
|
+
const button = container.querySelector('button');
|
|
407
|
+
|
|
408
|
+
// Initial state
|
|
409
|
+
expect(container.querySelector('pre').textContent).toBe('https://example.com/path');
|
|
410
|
+
|
|
411
|
+
// Change all properties
|
|
412
|
+
button.click();
|
|
413
|
+
flushSync();
|
|
414
|
+
|
|
415
|
+
expect(container.querySelector('pre').textContent).toBe('http://newdomain.com:8080/api?key=value#section');
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it('handles href change updates all properties and searchParams', () => {
|
|
419
|
+
component URLTest() {
|
|
420
|
+
const url = new TrackedURL('https://old.com/old?foo=bar#old');
|
|
421
|
+
const params = url.searchParams;
|
|
422
|
+
|
|
423
|
+
<button onClick={() => url.href = 'https://new.com:9090/new?baz=qux#new'}>{'Change Href'}</button>
|
|
424
|
+
<pre>{params.get('foo')}</pre>
|
|
425
|
+
<pre>{params.get('baz')}</pre>
|
|
426
|
+
<pre>{params.size}</pre>
|
|
427
|
+
<pre>{url.pathname}</pre>
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
render(URLTest);
|
|
431
|
+
|
|
432
|
+
const button = container.querySelector('button');
|
|
433
|
+
|
|
434
|
+
// Initial state
|
|
435
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('bar');
|
|
436
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('');
|
|
437
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('1');
|
|
438
|
+
expect(container.querySelectorAll('pre')[3].textContent).toBe('/old');
|
|
439
|
+
|
|
440
|
+
// Change href
|
|
441
|
+
button.click();
|
|
442
|
+
flushSync();
|
|
443
|
+
|
|
444
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('');
|
|
445
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('qux');
|
|
446
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('1');
|
|
447
|
+
expect(container.querySelectorAll('pre')[3].textContent).toBe('/new');
|
|
448
|
+
});
|
|
449
|
+
});
|