From ed3bcaa5747950820da8bea2f6bbc2a6e85d8b2e Mon Sep 17 00:00:00 2001 From: Yura Date: Fri, 1 Oct 2021 12:43:37 +0200 Subject: [PATCH] 83631: Reword & explain theme extension tests --- .../metadata-representation.decorator.spec.ts | 42 ++++++++++++------- .../listable-object.decorator.spec.ts | 42 ++++++++++++------- src/environments/environment.common.ts | 13 ++++++ 3 files changed, 67 insertions(+), 30 deletions(-) diff --git a/src/app/shared/metadata-representation/metadata-representation.decorator.spec.ts b/src/app/shared/metadata-representation/metadata-representation.decorator.spec.ts index a9988aad6f..d6239401d4 100644 --- a/src/app/shared/metadata-representation/metadata-representation.decorator.spec.ts +++ b/src/app/shared/metadata-representation/metadata-representation.decorator.spec.ts @@ -16,8 +16,8 @@ describe('MetadataRepresentation decorator function', () => { const type2 = 'TestType2'; const type3 = 'TestType3'; const type4 = 'RandomType'; - const typeHier1 = 'TestTypeHier1'; - const typeHier2 = 'TestTypeHier2'; + const typeAncestor = 'TestTypeAncestor'; + const typeUnthemed = 'TestTypeUnthemed'; let prefix; /* tslint:disable:max-classes-per-file */ @@ -36,10 +36,10 @@ describe('MetadataRepresentation decorator function', () => { class Test3ItemSubmission { } - class TestHier1Ancestor { + class TestAncestorComponent { } - class TestHier2Unthemed { + class TestUnthemedComponent { } /* tslint:enable:max-classes-per-file */ @@ -58,8 +58,9 @@ describe('MetadataRepresentation decorator function', () => { metadataRepresentationComponent(key + type3, MetadataRepresentationType.Item, Context.Workspace)(Test3ItemSubmission); - metadataRepresentationComponent(key + typeHier1, MetadataRepresentationType.Item, Context.Any, 'ancestor')(TestHier1Ancestor); - metadataRepresentationComponent(key + typeHier2, MetadataRepresentationType.Item, Context.Any)(TestHier2Unthemed); + // Register a metadata representation in the 'ancestor' theme + metadataRepresentationComponent(key + typeAncestor, MetadataRepresentationType.Item, Context.Any, 'ancestor')(TestAncestorComponent); + metadataRepresentationComponent(key + typeUnthemed, MetadataRepresentationType.Item, Context.Any)(TestUnthemedComponent); ogEnvironmentThemes = environment.themes; } @@ -98,22 +99,33 @@ describe('MetadataRepresentation decorator function', () => { }); describe('With theme extensions', () => { + // We're only interested in the cases that the requested theme doesn't match the requested entityType, + // as the cases where it does are already covered by the tests above describe('If requested theme has no match', () => { beforeEach(() => { environment.themes = [ - { name: 'requested', extends: 'intermediate' }, - { name: 'intermediate', extends: 'ancestor' }, + { + name: 'requested', // Doesn't match any entityType + extends: 'intermediate', + }, + { + name: 'intermediate', // Doesn't match any entityType + extends: 'ancestor', + }, + { + name: 'ancestor', // Matches typeAncestor, but not typeUnthemed + } ]; }); - it('should return component from ancestor theme if it has a match', () => { - const component = getMetadataRepresentationComponent(prefix + typeHier1, MetadataRepresentationType.Item, Context.Any, 'requested'); - expect(component).toEqual(TestHier1Ancestor); + it('should return component from the first ancestor theme that matches its entityType', () => { + const component = getMetadataRepresentationComponent(prefix + typeAncestor, MetadataRepresentationType.Item, Context.Any, 'requested'); + expect(component).toEqual(TestAncestorComponent); }); - it('should return default component if ancestor theme has no match', () => { - const component = getMetadataRepresentationComponent(prefix + typeHier2, MetadataRepresentationType.Item, Context.Any, 'requested'); - expect(component).toEqual(TestHier2Unthemed); + it('should return default component if none of the ancestor themes match its entityType', () => { + const component = getMetadataRepresentationComponent(prefix + typeUnthemed, MetadataRepresentationType.Item, Context.Any, 'requested'); + expect(component).toEqual(TestUnthemedComponent); }); }); @@ -129,7 +141,7 @@ describe('MetadataRepresentation decorator function', () => { it('should throw an error', () => { expect(() => { - getMetadataRepresentationComponent(prefix + typeHier1, MetadataRepresentationType.Item, Context.Any, 'extension-cycle'); + getMetadataRepresentationComponent(prefix + typeAncestor, MetadataRepresentationType.Item, Context.Any, 'extension-cycle'); }).toThrowError( 'Theme extension cycle detected: extension-cycle -> broken1 -> broken2 -> broken3 -> broken1' ); diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object.decorator.spec.ts b/src/app/shared/object-collection/shared/listable-object/listable-object.decorator.spec.ts index d3c7a5a1e1..05302c4bd4 100644 --- a/src/app/shared/object-collection/shared/listable-object/listable-object.decorator.spec.ts +++ b/src/app/shared/object-collection/shared/listable-object/listable-object.decorator.spec.ts @@ -10,8 +10,8 @@ describe('ListableObject decorator function', () => { const type1 = 'TestType'; const type2 = 'TestType2'; const type3 = 'TestType3'; - const typeHier1 = 'TestTypeHier1'; - const typeHier2 = 'TestTypeHier2'; + const typeAncestor = 'TestTypeAncestor'; + const typeUnthemed = 'TestTypeUnthemed'; /* tslint:disable:max-classes-per-file */ class Test1List { @@ -32,10 +32,10 @@ describe('ListableObject decorator function', () => { class Test3DetailedSubmission { } - class TestHier1Ancestor { + class TestAncestorComponent { } - class TestHier2Unthemed { + class TestUnthemedComponent { } /* tslint:enable:max-classes-per-file */ @@ -50,8 +50,9 @@ describe('ListableObject decorator function', () => { listableObjectComponent(type3, ViewMode.ListElement)(Test3List); listableObjectComponent(type3, ViewMode.DetailedListElement, Context.Workspace)(Test3DetailedSubmission); - listableObjectComponent(typeHier1, ViewMode.ListElement, Context.Any, 'ancestor')(TestHier1Ancestor); - listableObjectComponent(typeHier2, ViewMode.ListElement, Context.Any)(TestHier2Unthemed); + // Register a metadata representation in the 'ancestor' theme + listableObjectComponent(typeAncestor, ViewMode.ListElement, Context.Any, 'ancestor')(TestAncestorComponent); + listableObjectComponent(typeUnthemed, ViewMode.ListElement, Context.Any)(TestUnthemedComponent); ogEnvironmentThemes = environment.themes; }); @@ -102,22 +103,33 @@ describe('ListableObject decorator function', () => { }); describe('With theme extensions', () => { + // We're only interested in the cases that the requested theme doesn't match the requested objectType, + // as the cases where it does are already covered by the tests above describe('If requested theme has no match', () => { beforeEach(() => { environment.themes = [ - { name: 'requested', extends: 'intermediate' }, - { name: 'intermediate', extends: 'ancestor' }, + { + name: 'requested', // Doesn't match any objectType + extends: 'intermediate', + }, + { + name: 'intermediate', // Doesn't match any objectType + extends: 'ancestor', + }, + { + name: 'ancestor', // Matches typeAncestor, but not typeUnthemed + } ]; }); - it('should return component from ancestor theme if it has a match', () => { - const component = getListableObjectComponent([typeHier1], ViewMode.ListElement, Context.Any, 'requested'); - expect(component).toEqual(TestHier1Ancestor); + it('should return component from the first ancestor theme that matches its objectType', () => { + const component = getListableObjectComponent([typeAncestor], ViewMode.ListElement, Context.Any, 'requested'); + expect(component).toEqual(TestAncestorComponent); }); - it('should return default component if ancestor theme has no match', () => { - const component = getListableObjectComponent([typeHier2], ViewMode.ListElement, Context.Any, 'requested'); - expect(component).toEqual(TestHier2Unthemed); + it('should return default component if none of the ancestor themes match its objectType', () => { + const component = getListableObjectComponent([typeUnthemed], ViewMode.ListElement, Context.Any, 'requested'); + expect(component).toEqual(TestUnthemedComponent); }); }); @@ -133,7 +145,7 @@ describe('ListableObject decorator function', () => { it('should throw an error', () => { expect(() => { - getListableObjectComponent([typeHier1], ViewMode.ListElement, Context.Any, 'extension-cycle'); + getListableObjectComponent([typeAncestor], ViewMode.ListElement, Context.Any, 'extension-cycle'); }).toThrowError( 'Theme extension cycle detected: extension-cycle -> broken1 -> broken2 -> broken3 -> broken1' ); diff --git a/src/environments/environment.common.ts b/src/environments/environment.common.ts index 9091773041..f6fd7f1e68 100644 --- a/src/environments/environment.common.ts +++ b/src/environments/environment.common.ts @@ -265,6 +265,19 @@ export const environment: GlobalConfig = { // uuid: '0958c910-2037-42a9-81c7-dca80e3892b4' // }, // { + // // The extends property specifies an ancestor theme (by name). Whenever a themed component is not found + // // in the current theme, its ancestor theme(s) will be checked recursively before falling back to default. + // name: 'custom-A', + // extends: 'custom-B', + // // Any of the matching properties above can be used + // handle: '10673/34', + // }, + // { + // name: 'custom-B', + // extends: 'custom', + // handle: '10673/12', + // }, + // { // // A theme with only a name will match every route // name: 'custom' // },