forked from openshift/console
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support code references at any level within extension's properties
- Loading branch information
1 parent
23e8d0d
commit c79e077
Showing
16 changed files
with
237 additions
and
126 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,12 +11,9 @@ import { | |
applyCodeRefSymbol, | ||
isEncodedCodeRef, | ||
isExecutableCodeRef, | ||
filterEncodedCodeRefProperties, | ||
filterExecutableCodeRefProperties, | ||
parseEncodedCodeRefValue, | ||
loadReferencedObject, | ||
resolveEncodedCodeRefs, | ||
resolveCodeRefProperties, | ||
resolveExtension, | ||
} from '../coderef-resolver'; | ||
|
||
|
@@ -52,38 +49,6 @@ describe('isExecutableCodeRef', () => { | |
}); | ||
}); | ||
|
||
describe('filterEncodedCodeRefProperties', () => { | ||
it('picks properties whose values match isEncodedCodeRef predicate', () => { | ||
expect( | ||
filterEncodedCodeRefProperties({ | ||
foo: { $codeRef: 'foo' }, | ||
bar: ['test'], | ||
baz: () => {}, | ||
qux: getExecutableCodeRefMock('qux'), | ||
}), | ||
).toEqual({ | ||
foo: { $codeRef: 'foo' }, | ||
}); | ||
}); | ||
}); | ||
|
||
describe('filterExecutableCodeRefProperties', () => { | ||
it('picks properties whose values match isExecutableCodeRef predicate', () => { | ||
const ref = getExecutableCodeRefMock('qux'); | ||
|
||
expect( | ||
filterExecutableCodeRefProperties({ | ||
foo: { $codeRef: 'foo' }, | ||
bar: ['test'], | ||
baz: () => {}, | ||
qux: ref, | ||
}), | ||
).toEqual({ | ||
qux: ref, | ||
}); | ||
}); | ||
}); | ||
|
||
describe('parseEncodedCodeRefValue', () => { | ||
it('returns [moduleName, exportName] tuple if value has the right format', () => { | ||
expect(parseEncodedCodeRefValue('foo.bar')).toEqual(['foo', 'bar']); | ||
|
@@ -208,20 +173,26 @@ describe('loadReferencedObject', () => { | |
}); | ||
|
||
describe('resolveEncodedCodeRefs', () => { | ||
it('replaces encoded code references with CodeRef functions', () => { | ||
it('replaces encoded code references with CodeRef functions', async () => { | ||
const extensions: Extension[] = [ | ||
{ | ||
type: 'Foo', | ||
properties: { test: true }, | ||
properties: { | ||
test: true, | ||
qux: { $codeRef: 'mod.a' }, | ||
}, | ||
}, | ||
{ | ||
type: 'Bar', | ||
properties: { baz: 1, qux: { $codeRef: 'a.b' } }, | ||
properties: { | ||
test: [1], | ||
baz: { test: { $codeRef: 'mod.b' } }, | ||
}, | ||
}, | ||
]; | ||
|
||
const errorCallback = jest.fn(); | ||
const [, entryModule] = getEntryModuleMocks({ b: 'value' }); | ||
const [, entryModule] = getEntryModuleMocks({ a: 'value1', b: 'value2' }); | ||
|
||
const resolvedExtensions = resolveEncodedCodeRefs( | ||
extensions, | ||
|
@@ -231,62 +202,93 @@ describe('resolveEncodedCodeRefs', () => { | |
); | ||
|
||
expect(resolvedExtensions.length).toBe(extensions.length); | ||
expect(resolvedExtensions[0]).toEqual(extensions[0]); | ||
|
||
expect(_.omit(resolvedExtensions[1], 'properties.qux')).toEqual( | ||
_.omit(extensions[1], 'properties.qux'), | ||
); | ||
expect(resolvedExtensions[0].properties.test).toBe(true); | ||
expect(isExecutableCodeRef(resolvedExtensions[0].properties.qux)).toBe(true); | ||
expect(await resolvedExtensions[0].properties.qux()).toBe('value1'); | ||
|
||
expect(isExecutableCodeRef(resolvedExtensions[1].properties.qux)).toBe(true); | ||
expect(resolvedExtensions[1].properties.test).toEqual([1]); | ||
expect(isExecutableCodeRef(resolvedExtensions[1].properties.baz.test)).toBe(true); | ||
expect(await resolvedExtensions[1].properties.baz.test()).toBe('value2'); | ||
}); | ||
}); | ||
|
||
describe('resolveCodeRefProperties', () => { | ||
it('replaces CodeRef functions with referenced objects', async () => { | ||
it('clones the provided extensions array and its elements', () => { | ||
const extensions: Extension[] = [ | ||
{ | ||
type: 'Foo', | ||
properties: { test: true }, | ||
}, | ||
{ | ||
type: 'Bar', | ||
properties: { baz: 1, qux: getExecutableCodeRefMock('value') }, | ||
}, | ||
{ type: 'Foo', properties: { test: true } }, | ||
{ type: 'Bar', properties: { test: [1] } }, | ||
]; | ||
|
||
expect(await resolveCodeRefProperties(extensions[0])).toEqual({ test: true }); | ||
expect(await resolveCodeRefProperties(extensions[1])).toEqual({ baz: 1, qux: 'value' }); | ||
const errorCallback = jest.fn(); | ||
const [, entryModule] = getEntryModuleMocks({}); | ||
|
||
const resolvedExtensions = resolveEncodedCodeRefs( | ||
extensions, | ||
entryModule, | ||
'[email protected]', | ||
errorCallback, | ||
); | ||
|
||
expect(resolvedExtensions).not.toBe(extensions); | ||
expect(resolvedExtensions).toEqual(extensions); | ||
|
||
resolvedExtensions.forEach((e, index) => { | ||
expect(e).not.toBe(extensions[index]); | ||
expect(e).toEqual(extensions[index]); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('resolveExtension', () => { | ||
it('returns an extension with CodeRef functions replaced with referenced objects', async () => { | ||
it('replaces CodeRef functions with referenced objects', async () => { | ||
const extensions: Extension[] = [ | ||
{ | ||
type: 'Foo', | ||
properties: { test: true }, | ||
properties: { | ||
test: true, | ||
qux: getExecutableCodeRefMock('value1'), | ||
}, | ||
}, | ||
{ | ||
type: 'Bar', | ||
properties: { baz: 1, qux: getExecutableCodeRefMock('value') }, | ||
properties: { | ||
test: [1], | ||
baz: { test: getExecutableCodeRefMock('value2') }, | ||
}, | ||
}, | ||
]; | ||
|
||
expect(await resolveExtension(extensions[0])).toEqual({ | ||
type: 'Foo', | ||
properties: { test: true }, | ||
properties: { | ||
test: true, | ||
qux: 'value1', | ||
}, | ||
}); | ||
|
||
expect(await resolveExtension(extensions[1])).toEqual({ | ||
type: 'Bar', | ||
properties: { baz: 1, qux: 'value' }, | ||
properties: { | ||
test: [1], | ||
baz: { test: 'value2' }, | ||
}, | ||
}); | ||
}); | ||
|
||
it('returns a new extension instance', async () => { | ||
const testExtension: Extension = { type: 'Foo/Bar', properties: {} }; | ||
const resolvedExtension = await resolveExtension(testExtension); | ||
it('returns the same extension instance if it has no CodeRef functions', async () => { | ||
const e: Extension = { | ||
type: 'Foo', | ||
properties: { test: true }, | ||
}; | ||
|
||
expect(await resolveExtension(e)).toBe(e); | ||
}); | ||
|
||
it('returns a new extension instance if it has CodeRef functions', async () => { | ||
const e: Extension = { | ||
type: 'Foo', | ||
properties: { test: true, qux: getExecutableCodeRefMock('value1') }, | ||
}; | ||
|
||
expect(resolvedExtension).not.toBe(testExtension); | ||
expect(Object.isFrozen(resolvedExtension)).toBe(true); | ||
expect(await resolveExtension(e)).not.toBe(e); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
frontend/packages/console-dynamic-plugin-sdk/src/utils/__tests__/object.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import * as _ from 'lodash'; | ||
import { deepForOwn } from '../object'; | ||
|
||
type TestValue = { test: number }; | ||
|
||
const testPredicate = (value): value is TestValue => | ||
_.isPlainObject(value) && | ||
_.isEqual(Object.getOwnPropertyNames(value), ['test']) && | ||
typeof value.test === 'number'; | ||
|
||
describe('deepForOwn', () => { | ||
it('recursively iterates over matching property values', () => { | ||
const obj = { | ||
foo: { test: 1 }, | ||
bar: { | ||
qux: { test: 2 }, | ||
mux: { test: 3, boom: true }, | ||
}, | ||
}; | ||
|
||
const valueCallback = jest.fn(); | ||
|
||
deepForOwn<TestValue>(obj, testPredicate, valueCallback); | ||
|
||
expect(valueCallback.mock.calls.length).toBe(2); | ||
expect(valueCallback.mock.calls[0]).toEqual([{ test: 1 }, 'foo', obj]); | ||
expect(valueCallback.mock.calls[1]).toEqual([{ test: 2 }, 'qux', obj.bar]); | ||
}); | ||
|
||
it('does not iterate over array elements', () => { | ||
const obj = { | ||
foo: [{ test: 1 }], | ||
bar: { | ||
qux: [{ test: 2 }], | ||
mux: [{ test: 3, boom: true }], | ||
}, | ||
}; | ||
|
||
const valueCallback = jest.fn(); | ||
|
||
deepForOwn<TestValue>(obj, testPredicate, valueCallback); | ||
|
||
expect(valueCallback).not.toHaveBeenCalled(); | ||
}); | ||
}); |
16 changes: 16 additions & 0 deletions
16
frontend/packages/console-dynamic-plugin-sdk/src/utils/__tests__/url.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { resolveURL } from '../url'; | ||
|
||
describe('resolveURL', () => { | ||
const getDocumentOrigin = () => 'https://example:1234'; | ||
|
||
it('uses the base URL as-is if it has the protocol', () => { | ||
expect(resolveURL('http://test', 'foo', getDocumentOrigin)).toBe('http://test/foo'); | ||
expect(resolveURL('http://test/', 'foo', getDocumentOrigin)).toBe('http://test/foo'); | ||
expect(resolveURL('http://test/foo/', 'bar', getDocumentOrigin)).toBe('http://test/foo/bar'); | ||
}); | ||
|
||
it("makes the base URL relative to document origin if it's missing the protocol", () => { | ||
expect(resolveURL('/', 'foo', getDocumentOrigin)).toBe('https://example:1234/foo'); | ||
expect(resolveURL('/foo/', 'bar', getDocumentOrigin)).toBe('https://example:1234/foo/bar'); | ||
}); | ||
}); |
Oops, something went wrong.