1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 | import { enableJSDOM } from '../test/jsdom';
|
20 |
|
21 | let disableJSDOM = enableJSDOM();
|
22 |
|
23 | import * as assert from 'assert';
|
24 | import { Container } from 'inversify';
|
25 | import { bindPreferenceService } from '../frontend-application-bindings';
|
26 | import { bindMockPreferenceProviders, MockPreferenceProvider } from './test';
|
27 | import { PreferenceService, PreferenceServiceImpl } from './preference-service';
|
28 | import { PreferenceSchemaProvider, PreferenceSchema } from './preference-contribution';
|
29 | import { PreferenceScope } from './preference-scope';
|
30 | import { PreferenceProvider } from './preference-provider';
|
31 | import { FrontendApplicationConfigProvider } from '../frontend-application-config-provider';
|
32 | import { PreferenceProxyOptions, PreferenceProxy, PreferenceChangeEvent, createPreferenceProxy } from './preference-proxy';
|
33 | import { PreferenceProxyFactory } from './injectable-preference-proxy';
|
34 | import { waitForEvent } from '../../common/promise-util';
|
35 |
|
36 | disableJSDOM();
|
37 |
|
38 | process.on('unhandledRejection', (reason, promise) => {
|
39 | console.error(reason);
|
40 | throw reason;
|
41 | });
|
42 |
|
43 | import { expect } from 'chai';
|
44 | let testContainer: Container;
|
45 |
|
46 | function createTestContainer(): Container {
|
47 | const result = new Container();
|
48 | bindPreferenceService(result.bind.bind(result));
|
49 | bindMockPreferenceProviders(result.bind.bind(result), result.unbind.bind(result));
|
50 | return result;
|
51 | }
|
52 |
|
53 | describe('Preference Proxy', () => {
|
54 | let prefService: PreferenceServiceImpl;
|
55 | let prefSchema: PreferenceSchemaProvider;
|
56 |
|
57 | before(() => {
|
58 | disableJSDOM = enableJSDOM();
|
59 | FrontendApplicationConfigProvider.set({});
|
60 | });
|
61 |
|
62 | after(() => {
|
63 | disableJSDOM();
|
64 | });
|
65 |
|
66 | beforeEach(async () => {
|
67 | testContainer = createTestContainer();
|
68 | prefSchema = testContainer.get(PreferenceSchemaProvider);
|
69 | prefService = testContainer.get<PreferenceService>(PreferenceService) as PreferenceServiceImpl;
|
70 | getProvider(PreferenceScope.User).markReady();
|
71 | getProvider(PreferenceScope.Workspace).markReady();
|
72 | getProvider(PreferenceScope.Folder).markReady();
|
73 | try {
|
74 | await prefService.ready;
|
75 | } catch (e) {
|
76 | console.error(e);
|
77 | }
|
78 | });
|
79 |
|
80 | afterEach(() => {
|
81 | });
|
82 |
|
83 |
|
84 | testPreferenceProxy('Synchronous Schema Definition + createPreferenceProxy', { asyncSchema: false });
|
85 | testPreferenceProxy('Asynchronous Schema Definition (1s delay) + createPreferenceProxy', { asyncSchema: true });
|
86 | testPreferenceProxy('Synchronous Schema Definition + Injectable Preference Proxy', { asyncSchema: false, useFactory: true });
|
87 | testPreferenceProxy('Asynchronous Schema Definition (1s delay) + Injectable Preference Proxy', { asyncSchema: true, useFactory: true });
|
88 |
|
89 | function getProvider(scope: PreferenceScope): MockPreferenceProvider {
|
90 | return testContainer.getNamed(PreferenceProvider, scope) as MockPreferenceProvider;
|
91 | }
|
92 |
|
93 | function testPreferenceProxy(testDescription: string, testOptions: { asyncSchema: boolean, useFactory?: boolean }): void {
|
94 |
|
95 | describe(testDescription, () => {
|
96 |
|
97 | function getProxy(schema?: PreferenceSchema, options?: PreferenceProxyOptions): {
|
98 | proxy: PreferenceProxy<{ [key: string]: any }>,
|
99 |
|
100 | promisedSchema?: Promise<PreferenceSchema>
|
101 | } {
|
102 | const s: PreferenceSchema = schema || {
|
103 | properties: {
|
104 | 'my.pref': {
|
105 | type: 'string',
|
106 | defaultValue: 'foo'
|
107 | }
|
108 | }
|
109 | };
|
110 | if (testOptions.asyncSchema) {
|
111 | const promisedSchema = new Promise<PreferenceSchema>(resolve => setTimeout(() => {
|
112 | prefSchema.setSchema(s);
|
113 | resolve(s);
|
114 | }, 1000));
|
115 | const proxy = testOptions.useFactory
|
116 | ? testContainer.get<PreferenceProxyFactory>(PreferenceProxyFactory)(promisedSchema, options)
|
117 | : createPreferenceProxy(prefService, promisedSchema, options);
|
118 | return { proxy, promisedSchema };
|
119 | } else {
|
120 | prefSchema.setSchema(s);
|
121 | const proxy = testOptions.useFactory
|
122 | ? testContainer.get<PreferenceProxyFactory>(PreferenceProxyFactory)(s, options)
|
123 | : createPreferenceProxy(prefService, s, options);
|
124 | return { proxy };
|
125 | }
|
126 | }
|
127 |
|
128 | if (testOptions.asyncSchema) {
|
129 | it('using the proxy before the schema is set should be no-op', async () => {
|
130 | const { proxy, promisedSchema } = getProxy();
|
131 | let changed = 0;
|
132 | proxy.onPreferenceChanged(event => {
|
133 | changed += 1;
|
134 | });
|
135 | expect(proxy['my.pref']).to.equal(undefined);
|
136 | expect(Object.keys(proxy).length).to.equal(0);
|
137 |
|
138 | await getProvider(PreferenceScope.User).setPreference('my.pref', 'bar');
|
139 | expect(changed).to.equal(0);
|
140 | expect(proxy['my.pref']).to.equal(undefined);
|
141 | expect(Object.keys(proxy).length).to.equal(0);
|
142 |
|
143 | await promisedSchema!;
|
144 | expect(proxy['my.pref']).to.equal('bar');
|
145 | expect(Object.keys(proxy)).members(['my.pref']);
|
146 | await getProvider(PreferenceScope.User).setPreference('my.pref', 'fizz');
|
147 | expect(changed).to.equal(1);
|
148 | expect(proxy['my.pref']).to.equal('fizz');
|
149 |
|
150 | });
|
151 | }
|
152 |
|
153 | it('by default, it should provide access in flat style but not deep', async () => {
|
154 | const { proxy, promisedSchema } = getProxy();
|
155 | if (promisedSchema) {
|
156 | await promisedSchema;
|
157 | }
|
158 | expect(proxy['my.pref']).to.equal('foo');
|
159 | expect(proxy.my).to.equal(undefined);
|
160 | expect(Object.keys(proxy).join()).to.equal(['my.pref'].join());
|
161 | });
|
162 |
|
163 | it('it should provide access in deep style but not flat', async () => {
|
164 | const { proxy, promisedSchema } = getProxy(undefined, { style: 'deep' });
|
165 | if (promisedSchema) {
|
166 | await promisedSchema;
|
167 | }
|
168 | expect(proxy['my.pref']).to.equal(undefined);
|
169 | expect(proxy.my.pref).to.equal('foo');
|
170 | expect(Object.keys(proxy).join()).equal('my');
|
171 | });
|
172 |
|
173 | it('it should provide access in to both styles', async () => {
|
174 | const { proxy, promisedSchema } = getProxy(undefined, { style: 'both' });
|
175 | if (promisedSchema) {
|
176 | await promisedSchema;
|
177 | }
|
178 | expect(proxy['my.pref']).to.equal('foo');
|
179 | expect(proxy.my.pref).to.equal('foo');
|
180 | expect(Object.keys(proxy).join()).to.equal(['my', 'my.pref'].join());
|
181 | });
|
182 |
|
183 | it('it should forward change events', async () => {
|
184 | const { proxy, promisedSchema } = getProxy(undefined, { style: 'both' });
|
185 | if (promisedSchema) {
|
186 | await promisedSchema;
|
187 | }
|
188 | let theChange: PreferenceChangeEvent<{ [key: string]: any }>;
|
189 | proxy.onPreferenceChanged(change => {
|
190 | expect(theChange).to.equal(undefined);
|
191 | theChange = change;
|
192 | });
|
193 | let theSecondChange: PreferenceChangeEvent<{ [key: string]: any }>;
|
194 | (proxy.my as PreferenceProxy<{ [key: string]: any }>).onPreferenceChanged(change => {
|
195 | expect(theSecondChange).to.equal(undefined);
|
196 | theSecondChange = change;
|
197 | });
|
198 |
|
199 | await getProvider(PreferenceScope.User).setPreference('my.pref', 'bar');
|
200 |
|
201 | expect(theChange!.newValue).to.equal('bar');
|
202 | expect(theChange!.oldValue).to.equal(undefined);
|
203 | expect(theChange!.preferenceName).to.equal('my.pref');
|
204 | expect(theSecondChange!.newValue).to.equal('bar');
|
205 | expect(theSecondChange!.oldValue).to.equal(undefined);
|
206 | expect(theSecondChange!.preferenceName).to.equal('my.pref');
|
207 | });
|
208 |
|
209 | it("should not forward changes that don't match the proxy's language override", async () => {
|
210 | const { proxy, promisedSchema } = getProxy({
|
211 | properties: {
|
212 | 'my.pref': {
|
213 | type: 'string',
|
214 | defaultValue: 'foo',
|
215 | overridable: true,
|
216 | }
|
217 | }
|
218 | }, { style: 'both', overrideIdentifier: 'typescript' });
|
219 | await promisedSchema;
|
220 | let changeEventsEmittedByProxy = 0;
|
221 | let changeEventsEmittedByService = 0;
|
222 | prefSchema.registerOverrideIdentifier('swift');
|
223 | prefSchema.registerOverrideIdentifier('typescript');
|
224 |
|
225 | await waitForEvent(prefService.onPreferencesChanged, 500);
|
226 | prefService.onPreferencesChanged(() => changeEventsEmittedByService++);
|
227 | proxy.onPreferenceChanged(() => changeEventsEmittedByProxy++);
|
228 | await prefService.set(prefService.overridePreferenceName({ overrideIdentifier: 'swift', preferenceName: 'my.pref' }), 'boo', PreferenceScope.User);
|
229 | expect(changeEventsEmittedByService, 'The service should have emitted an event for the non-matching override.').to.equal(1);
|
230 | expect(changeEventsEmittedByProxy, 'The proxy should not have emitted an event for the non-matching override.').to.equal(0);
|
231 | await prefService.set('my.pref', 'far', PreferenceScope.User);
|
232 | expect(changeEventsEmittedByService, 'The service should have emitted an event for the base name.').to.equal(2);
|
233 | expect(changeEventsEmittedByProxy, 'The proxy should have emitted for an event for the base name.').to.equal(1);
|
234 | await prefService.set(prefService.overridePreferenceName({ preferenceName: 'my.pref', overrideIdentifier: 'typescript' }), 'faz', PreferenceScope.User);
|
235 | expect(changeEventsEmittedByService, 'The service should have emitted an event for the matching override.').to.equal(3);
|
236 | expect(changeEventsEmittedByProxy, 'The proxy should have emitted an event for the matching override.').to.equal(2);
|
237 | await prefService.set('my.pref', 'yet another value', PreferenceScope.User);
|
238 | expect(changeEventsEmittedByService, 'The service should have emitted another event for the base name.').to.equal(4);
|
239 | expect(changeEventsEmittedByProxy, 'The proxy should not have emitted an event, because the value for TS has been overridden.').to.equal(2);
|
240 | });
|
241 |
|
242 | it('`affects` should only return `true` if the language overrides match', async () => {
|
243 | const { proxy, promisedSchema } = getProxy({
|
244 | properties: {
|
245 | 'my.pref': {
|
246 | type: 'string',
|
247 | defaultValue: 'foo',
|
248 | overridable: true,
|
249 | }
|
250 | }
|
251 | }, { style: 'both' });
|
252 | await promisedSchema;
|
253 | prefSchema.registerOverrideIdentifier('swift');
|
254 | prefSchema.registerOverrideIdentifier('typescript');
|
255 | let changesNotAffectingTypescript = 0;
|
256 | let changesAffectingTypescript = 0;
|
257 | proxy.onPreferenceChanged(change => {
|
258 | if (change.affects(undefined, 'typescript')) {
|
259 | changesAffectingTypescript++;
|
260 | } else {
|
261 | changesNotAffectingTypescript++;
|
262 | }
|
263 | });
|
264 | await prefService.set('my.pref', 'bog', PreferenceScope.User);
|
265 | expect(changesNotAffectingTypescript, 'Two events (one for `my.pref` and one for `[swift].my.pref`) should not have affected TS').to.equal(2);
|
266 | expect(changesAffectingTypescript, 'One event should have been fired that does affect typescript.').to.equal(1);
|
267 | });
|
268 |
|
269 | it('toJSON with deep', async () => {
|
270 | const { proxy, promisedSchema } = getProxy({
|
271 | properties: {
|
272 | 'foo.baz': {
|
273 | type: 'number',
|
274 | default: 4
|
275 | },
|
276 | 'foo.bar.x': {
|
277 | type: 'boolean',
|
278 | default: true
|
279 | },
|
280 | 'foo.bar.y': {
|
281 | type: 'boolean',
|
282 | default: false
|
283 | },
|
284 | 'a': {
|
285 | type: 'string',
|
286 | default: 'a'
|
287 | }
|
288 | }
|
289 | }, { style: 'deep' });
|
290 | if (promisedSchema) {
|
291 | await promisedSchema;
|
292 | }
|
293 | assert.deepStrictEqual(JSON.stringify(proxy, undefined, 2), JSON.stringify({
|
294 | foo: {
|
295 | baz: 4,
|
296 | bar: {
|
297 | x: true,
|
298 | y: false
|
299 | }
|
300 | },
|
301 | a: 'a'
|
302 | }, undefined, 2), 'there should not be foo.bar.x to avoid sending excessive data to remote clients');
|
303 | });
|
304 |
|
305 | it('get nested default', async () => {
|
306 | const { proxy, promisedSchema } = getProxy({
|
307 | properties: {
|
308 | 'foo': {
|
309 | 'anyOf': [
|
310 | {
|
311 | 'enum': [
|
312 | false
|
313 | ]
|
314 | },
|
315 | {
|
316 | 'properties': {
|
317 | 'bar': {
|
318 | 'anyOf': [
|
319 | {
|
320 | 'enum': [
|
321 | false
|
322 | ]
|
323 | },
|
324 | {
|
325 | 'properties': {
|
326 | 'x': {
|
327 | type: 'boolean'
|
328 | },
|
329 | 'y': {
|
330 | type: 'boolean'
|
331 | }
|
332 | }
|
333 | }
|
334 | ]
|
335 | }
|
336 | }
|
337 | }
|
338 | ],
|
339 | default: {
|
340 | bar: {
|
341 | x: true,
|
342 | y: false
|
343 | }
|
344 | }
|
345 | }
|
346 | }
|
347 | }, { style: 'both' });
|
348 | if (promisedSchema) {
|
349 | await promisedSchema;
|
350 | }
|
351 | assert.deepStrictEqual(proxy['foo'], {
|
352 | bar: {
|
353 | x: true,
|
354 | y: false
|
355 | }
|
356 | });
|
357 | assert.deepStrictEqual(proxy['foo.bar'], {
|
358 | x: true,
|
359 | y: false
|
360 | });
|
361 | assert.strictEqual(proxy['foo.bar.x'], true);
|
362 | assert.strictEqual(proxy['foo.bar.y'], false);
|
363 | });
|
364 | });
|
365 | }
|
366 |
|
367 | });
|
368 |
|
\ | No newline at end of file |