tests
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
import { expect, fixture } from '@open-wc/testing';
|
||||
import { UmbExtensionRegistry } from '../registry/extension.registry.js';
|
||||
import { ManifestApi, ManifestWithDynamicConditions } from '../types.js';
|
||||
import { UmbExtensionApiController } from './index.js';
|
||||
import { UmbBaseController, UmbControllerHost, UmbControllerHostElement, UmbControllerHostElementMixin } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { customElement, html } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { type ManifestSection, UmbSwitchCondition } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
@customElement('umb-test-controller-host')
|
||||
export class UmbTestControllerHostElement extends UmbControllerHostElementMixin(HTMLElement) {}
|
||||
|
||||
|
||||
export class UmbTestApiController extends UmbBaseController {
|
||||
|
||||
public i_am_test_api_controller = true;
|
||||
|
||||
constructor(host:UmbControllerHost) {
|
||||
super(host);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface TestManifest extends ManifestWithDynamicConditions, ManifestApi<UmbTestApiController> {
|
||||
type: 'test-type'
|
||||
}
|
||||
|
||||
describe('UmbExtensionApiController', () => {
|
||||
describe('Manifest without conditions', () => {
|
||||
let hostElement: UmbControllerHostElement;
|
||||
let extensionRegistry: UmbExtensionRegistry<TestManifest>;
|
||||
let manifest: TestManifest;
|
||||
|
||||
beforeEach(async () => {
|
||||
hostElement = await fixture(html`<umb-test-controller-host></umb-test-controller-host>`);
|
||||
extensionRegistry = new UmbExtensionRegistry();
|
||||
manifest = {
|
||||
type: 'test-type',
|
||||
name: 'test-type-1',
|
||||
alias: 'Umb.Test.Type-1',
|
||||
api: UmbTestApiController
|
||||
};
|
||||
|
||||
extensionRegistry.register(manifest);
|
||||
});
|
||||
|
||||
it('permits when there is no conditions', (done) => {
|
||||
let called = false;
|
||||
const extensionController = new UmbExtensionApiController<TestManifest>(
|
||||
hostElement,
|
||||
extensionRegistry,
|
||||
'Umb.Test.Type-1',
|
||||
[hostElement],
|
||||
(permitted) => {
|
||||
if (called === false) {
|
||||
called = true;
|
||||
expect(permitted).to.be.true;
|
||||
if (permitted) {
|
||||
expect(extensionController?.manifest?.alias).to.eq('Umb.Test.Type-1');
|
||||
expect(extensionController.api?.i_am_test_api_controller).to.be.true;
|
||||
done();
|
||||
extensionController.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
/*
|
||||
TODO: Consider if builder pattern would be a more nice way to setup this:
|
||||
const extensionController = new UmbExtensionApiController<TestManifest>(
|
||||
hostElement,
|
||||
extensionRegistry,
|
||||
'Umb.Test.Type-1'
|
||||
)
|
||||
.withConstructorArguments([hostElement])
|
||||
.onPermitted((permitted) => {
|
||||
if (called === false) {
|
||||
called = true;
|
||||
expect(permitted).to.be.true;
|
||||
if (permitted) {
|
||||
expect(extensionController?.manifest?.alias).to.eq('Umb.Test.Type-1');
|
||||
expect(extensionController.api?.i_am_test_api_controller).to.be.true;
|
||||
done();
|
||||
extensionController.destroy();
|
||||
}
|
||||
}
|
||||
).observe();
|
||||
*/
|
||||
});
|
||||
});
|
||||
|
||||
describe('Manifest with multiple conditions that changes over time', () => {
|
||||
let hostElement: UmbControllerHostElement;
|
||||
let extensionRegistry: UmbExtensionRegistry<ManifestSection>;
|
||||
let manifest: ManifestSection;
|
||||
|
||||
beforeEach(async () => {
|
||||
hostElement = await fixture(html`<umb-test-controller-host></umb-test-controller-host>`);
|
||||
extensionRegistry = new UmbExtensionRegistry();
|
||||
|
||||
manifest = {
|
||||
type: 'test-type',
|
||||
name: 'test-type-1',
|
||||
alias: 'Umb.Test.Type-1',
|
||||
api: UmbTestApiController,
|
||||
conditions: [
|
||||
{
|
||||
alias: 'Umb.Test.Condition.Delay',
|
||||
frequency: '100',
|
||||
},
|
||||
{
|
||||
alias: 'Umb.Test.Condition.Delay',
|
||||
frequency: '200',
|
||||
},
|
||||
],
|
||||
} as any;
|
||||
|
||||
// A ASCII timeline for the conditions, when allowed and then not allowed:
|
||||
// Condition 0ms 100ms 200ms 300ms 400ms 500ms
|
||||
// First condition: - + - + - +
|
||||
// Second condition: - - + + - -
|
||||
// Sum: - - - + - -
|
||||
|
||||
const conditionManifest = {
|
||||
type: 'condition',
|
||||
name: 'test-condition-delay',
|
||||
alias: 'Umb.Test.Condition.Delay',
|
||||
api: UmbSwitchCondition,
|
||||
};
|
||||
|
||||
extensionRegistry.register(manifest);
|
||||
extensionRegistry.register(conditionManifest);
|
||||
});
|
||||
|
||||
it('does change permission as conditions change', (done) => {
|
||||
let count = 0;
|
||||
const extensionController = new UmbExtensionApiController<TestManifest>(
|
||||
hostElement,
|
||||
extensionRegistry,
|
||||
'Umb.Test.Type-1',
|
||||
[hostElement],
|
||||
async () => {
|
||||
count++;
|
||||
// We want the controller callback to first fire when conditions are initialized.
|
||||
expect(extensionController.manifest?.conditions?.length).to.be.equal(2);
|
||||
expect(extensionController?.manifest?.alias).to.eq('Umb.Test.Type-1');
|
||||
if (count === 1) {
|
||||
expect(extensionController?.permitted).to.be.true;
|
||||
expect(extensionController.api?.i_am_test_api_controller).to.be.true;
|
||||
} else if (count === 2) {
|
||||
expect(extensionController?.permitted).to.be.false;
|
||||
expect(extensionController.api).to.be.undefined;
|
||||
done();
|
||||
extensionController.destroy(); // need to destroy the controller.
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -6,12 +6,13 @@ import { UmbBaseExtensionController } from './base-extension-controller.js';
|
||||
import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
export class UmbExtensionApiController<
|
||||
ManifestType extends (ManifestWithDynamicConditions & ManifestApi<ApiType>) = (ManifestWithDynamicConditions & ManifestApi<any>),
|
||||
ControllerType extends UmbExtensionApiController<ManifestType> = any,
|
||||
ApiType extends ExtensionApi = ExtensionApi,
|
||||
ManifestType extends (ManifestWithDynamicConditions & ManifestApi) = (ManifestWithDynamicConditions & ManifestApi),
|
||||
ControllerType extends UmbExtensionApiController<ManifestType, any> = any,
|
||||
ExtensionApiInterface extends ExtensionApi = ManifestType extends ManifestApi ? NonNullable<ManifestType['API_TYPE']> : ExtensionApi
|
||||
> extends UmbBaseExtensionController<ManifestType, ControllerType> {
|
||||
|
||||
_api?: ApiType;
|
||||
#api?: ExtensionApiInterface;
|
||||
#constructorArguments?: Array<unknown>;
|
||||
|
||||
/**
|
||||
* The api that is created for this extension.
|
||||
@@ -19,33 +20,9 @@ export class UmbExtensionApiController<
|
||||
* @type {(class | undefined)}
|
||||
*/
|
||||
public get api() {
|
||||
return this._api;
|
||||
return this.#api;
|
||||
}
|
||||
|
||||
/**
|
||||
* The arguments passed to the class constructor.
|
||||
* @type {Array<any>}
|
||||
* @memberof UmbApiExtensionController
|
||||
* @example
|
||||
* ```ts
|
||||
* const controller = new UmbApiExtensionController(host, extensionRegistry, alias, onPermissionChanged);
|
||||
* controller.props = { foo: 'bar' };
|
||||
* ```
|
||||
* Is equivalent to:
|
||||
* ```ts
|
||||
* controller.component.foo = 'bar';
|
||||
* ```
|
||||
*/
|
||||
#constructorArguments?: Array<unknown>;
|
||||
get constructorArguments() {
|
||||
return this.#constructorArguments;
|
||||
}
|
||||
set constructorArguments(newVal) {
|
||||
this.#constructorArguments = newVal;
|
||||
if(this._api) {
|
||||
console.warn('Constructor Arguments was set after api class has been constructed')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The props that are passed to the class.
|
||||
@@ -77,9 +54,11 @@ export class UmbExtensionApiController<
|
||||
host: UmbControllerHost,
|
||||
extensionRegistry: UmbExtensionRegistry<ManifestCondition>,
|
||||
alias: string,
|
||||
constructorArguments: Array<unknown> | undefined,
|
||||
onPermissionChanged: (isPermitted: boolean, controller: ControllerType) => void
|
||||
) {
|
||||
super(host, extensionRegistry, alias, onPermissionChanged);
|
||||
this.#constructorArguments = constructorArguments;
|
||||
this._init();
|
||||
}
|
||||
|
||||
@@ -98,18 +77,18 @@ export class UmbExtensionApiController<
|
||||
const manifest = this.manifest!; // In this case we are sure its not undefined.
|
||||
|
||||
if (isManifestApiType(manifest)) {
|
||||
const newApi = await createExtensionApi<ApiType>(manifest, this.#constructorArguments);
|
||||
const newApi = await createExtensionApi<ExtensionApiInterface>(manifest as unknown as ManifestApi<ExtensionApiInterface>, this.#constructorArguments);
|
||||
if (!this._positive) {
|
||||
// We are not positive anymore, so we will back out of this creation.
|
||||
return false;
|
||||
}
|
||||
this._api = newApi;
|
||||
this.#api = newApi;
|
||||
|
||||
} else {
|
||||
this._api = undefined;
|
||||
this.#api = undefined;
|
||||
console.warn('Manifest did not provide any useful data for a api class to construct.')
|
||||
}
|
||||
if (this._api) {
|
||||
if (this.#api) {
|
||||
//this.#assignProperties();
|
||||
return true; // we will confirm we have a component and are still good to go.
|
||||
}
|
||||
@@ -119,11 +98,11 @@ export class UmbExtensionApiController<
|
||||
|
||||
protected async _conditionsAreBad() {
|
||||
// Destroy the element:
|
||||
if (this._api) {
|
||||
if ('destroy' in this._api) {
|
||||
(this._api as unknown as { destroy: () => void }).destroy();
|
||||
if (this.#api) {
|
||||
if ('destroy' in this.#api) {
|
||||
(this.#api as unknown as { destroy: () => void }).destroy();
|
||||
}
|
||||
this._api = undefined;
|
||||
this.#api = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
export class UmbExtensionElementController<
|
||||
ManifestType extends ManifestWithDynamicConditions = ManifestWithDynamicConditions,
|
||||
ControllerType extends UmbExtensionElementController<ManifestType> = any
|
||||
ControllerType extends UmbExtensionElementController<ManifestType, any> = any
|
||||
> extends UmbBaseExtensionController<ManifestType, ControllerType> {
|
||||
_defaultElement?: string;
|
||||
_component?: HTMLElement;
|
||||
#defaultElement?: string;
|
||||
#component?: HTMLElement;
|
||||
|
||||
/**
|
||||
* The component that is created for this extension.
|
||||
@@ -18,7 +18,7 @@ export class UmbExtensionElementController<
|
||||
* @type {(HTMLElement | undefined)}
|
||||
*/
|
||||
public get component() {
|
||||
return this._component;
|
||||
return this.#component;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,16 +53,16 @@ export class UmbExtensionElementController<
|
||||
defaultElement?: string
|
||||
) {
|
||||
super(host, extensionRegistry, alias, onPermissionChanged);
|
||||
this._defaultElement = defaultElement;
|
||||
this.#defaultElement = defaultElement;
|
||||
this._init();
|
||||
}
|
||||
|
||||
#assignProperties = () => {
|
||||
if (!this._component || !this.#properties) return;
|
||||
if (!this.#component || !this.#properties) return;
|
||||
|
||||
// TODO: we could optimize this so we only re-set the updated props.
|
||||
Object.keys(this.#properties).forEach((key) => {
|
||||
(this._component as any)[key] = this.#properties![key];
|
||||
(this.#component as any)[key] = this.#properties![key];
|
||||
});
|
||||
};
|
||||
|
||||
@@ -70,22 +70,22 @@ export class UmbExtensionElementController<
|
||||
const manifest = this.manifest!; // In this case we are sure its not undefined.
|
||||
|
||||
if (isManifestElementableType(manifest)) {
|
||||
const newComponent = await createExtensionElement(manifest, this._defaultElement);
|
||||
const newComponent = await createExtensionElement(manifest, this.#defaultElement);
|
||||
if (!this._positive) {
|
||||
// We are not positive anymore, so we will back out of this creation.
|
||||
return false;
|
||||
}
|
||||
this._component = newComponent;
|
||||
this.#component = newComponent;
|
||||
|
||||
} else if (this._defaultElement) {
|
||||
this._component = document.createElement(this._defaultElement);
|
||||
} else if (this.#defaultElement) {
|
||||
this.#component = document.createElement(this.#defaultElement);
|
||||
} else {
|
||||
this._component = undefined;
|
||||
this.#component = undefined;
|
||||
console.warn('Manifest did not provide any useful data for a web component to be created.')
|
||||
}
|
||||
if (this._component) {
|
||||
if (this.#component) {
|
||||
this.#assignProperties();
|
||||
(this._component as any).manifest = manifest;
|
||||
(this.#component as any).manifest = manifest;
|
||||
return true; // we will confirm we have a component and are still good to go.
|
||||
}
|
||||
|
||||
@@ -94,11 +94,11 @@ export class UmbExtensionElementController<
|
||||
|
||||
protected async _conditionsAreBad() {
|
||||
// Destroy the element:
|
||||
if (this._component) {
|
||||
if ('destroy' in this._component) {
|
||||
(this._component as unknown as { destroy: () => void }).destroy();
|
||||
if (this.#component) {
|
||||
if ('destroy' in this.#component) {
|
||||
(this.#component as unknown as { destroy: () => void }).destroy();
|
||||
}
|
||||
this._component = undefined;
|
||||
this.#component = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,14 @@ import { UmbBaseExtensionController } from './base-extension-controller.js';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
export class UmbExtensionManifestController<
|
||||
ManifestType extends ManifestWithDynamicConditions = ManifestWithDynamicConditions
|
||||
> extends UmbBaseExtensionController<ManifestType> {
|
||||
ManifestType extends ManifestWithDynamicConditions = ManifestWithDynamicConditions,
|
||||
ControllerType extends UmbBaseExtensionController<ManifestType, any> = any
|
||||
> extends UmbBaseExtensionController<ManifestType, ControllerType> {
|
||||
constructor(
|
||||
host: UmbControllerHost,
|
||||
extensionRegistry: UmbExtensionRegistry<ManifestCondition>,
|
||||
alias: string,
|
||||
onPermissionChanged: (isPermitted: boolean, controller: UmbBaseExtensionController<ManifestType>) => void
|
||||
onPermissionChanged: (isPermitted: boolean, controller: ControllerType) => void
|
||||
) {
|
||||
super(host, extensionRegistry, alias, onPermissionChanged);
|
||||
this._init();
|
||||
|
||||
@@ -42,27 +42,19 @@ export class UmbExtensionsApiController<
|
||||
}
|
||||
*/
|
||||
|
||||
#constructorArgs?: Array<unknown>;
|
||||
|
||||
public get constructorArguments() {
|
||||
return this.#constructorArgs;
|
||||
}
|
||||
public set constructorArguments(args: Array<unknown> | undefined) {
|
||||
this.#constructorArgs = args;
|
||||
this._extensions.forEach((controller) => {
|
||||
controller.constructorArguments = args;
|
||||
});
|
||||
}
|
||||
#constructorArgs: Array<unknown> | undefined;
|
||||
|
||||
constructor(
|
||||
host: UmbControllerHost,
|
||||
extensionRegistry: UmbExtensionRegistry<ManifestTypes>,
|
||||
type: ManifestTypeName | Array<ManifestTypeName>,
|
||||
constructorArguments: Array<unknown> | undefined,
|
||||
filter: undefined | null | ((manifest: ManifestTypeAsApi) => boolean),
|
||||
onChange: (permittedManifests: Array<MyPermittedControllerType>, controller: MyPermittedControllerType) => void
|
||||
) {
|
||||
super(host, extensionRegistry, type, filter, onChange);
|
||||
this.#extensionRegistry = extensionRegistry;
|
||||
this.#constructorArgs = constructorArguments;
|
||||
this._init();
|
||||
}
|
||||
|
||||
@@ -71,11 +63,10 @@ export class UmbExtensionsApiController<
|
||||
this,
|
||||
this.#extensionRegistry,
|
||||
manifest.alias,
|
||||
this.#constructorArgs,
|
||||
this._extensionChanged
|
||||
) as ControllerType;
|
||||
|
||||
extController.constructorArguments = this.#constructorArgs;
|
||||
|
||||
return extController;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user