diff --git a/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/counter-workspace-context.test.ts b/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/counter-workspace-context.test.ts new file mode 100644 index 0000000000..9b23526ebc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/counter-workspace-context.test.ts @@ -0,0 +1,318 @@ +import { WorkspaceContextCounterElement, EXAMPLE_COUNTER_CONTEXT } from './counter-workspace-context.js'; +import { ExampleCounterWorkspaceView } from './counter-workspace-view.js'; +import { ExampleCounterStatusFooterAppElement } from './counter-status-footer-app.element.js'; +import { expect, fixture, defineCE } from '@open-wc/testing'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { html } from '@umbraco-cms/backoffice/external/lit'; + +class TestHostElement extends UmbLitElement {} +const testHostElement = defineCE(TestHostElement); + +describe('WorkspaceContextCounterElement', () => { + let element: UmbLitElement; + let context: WorkspaceContextCounterElement; + + beforeEach(async () => { + element = await fixture(`<${testHostElement}>`); + context = new WorkspaceContextCounterElement(element); + }); + + describe('Counter functionality', () => { + it('initializes with counter value of 0', (done) => { + context.counter.subscribe((value) => { + expect(value).to.equal(0); + done(); + }); + }); + + it('increments counter value when increment method is called', (done) => { + let callbackCount = 0; + + context.counter.subscribe((value) => { + callbackCount++; + if (callbackCount === 1) { + expect(value).to.equal(0); + context.increment(); + } else if (callbackCount === 2) { + expect(value).to.equal(1); + done(); + } + }); + }); + + it('increments counter multiple times correctly', (done) => { + let callbackCount = 0; + + context.counter.subscribe((value) => { + callbackCount++; + if (callbackCount === 1) { + expect(value).to.equal(0); + context.increment(); + context.increment(); + context.increment(); + } else if (callbackCount === 4) { + expect(value).to.equal(3); + done(); + } + }); + }); + }); + + describe('Reset functionality', () => { + it('resets counter to 0 when reset method is called', (done) => { + let callbackCount = 0; + + context.counter.subscribe((value) => { + callbackCount++; + if (callbackCount === 1) { + expect(value).to.equal(0); + // First increment the counter + context.increment(); + context.increment(); + } else if (callbackCount === 3) { + expect(value).to.equal(2); + // Then reset it + context.reset(); + } else if (callbackCount === 4) { + expect(value).to.equal(0); + done(); + } + }); + }); + + it('can reset from any counter value', (done) => { + let callbackCount = 0; + + context.counter.subscribe((value) => { + callbackCount++; + if (callbackCount === 1) { + expect(value).to.equal(0); + // Increment to a higher number + context.increment(); + context.increment(); + context.increment(); + context.increment(); + context.increment(); + } else if (callbackCount === 6) { + expect(value).to.equal(5); + context.reset(); + } else if (callbackCount === 7) { + expect(value).to.equal(0); + done(); + } + }); + }); + + it('reset works when counter is already at 0', (done) => { + context.counter.subscribe((value) => { + expect(value).to.equal(0); + // Reset when already at 0 - should not cause issues + context.reset(); + // Verify it's still 0 + expect(value).to.equal(0); + done(); + }); + }); + }); + + describe('Increment and Reset interaction', () => { + it('can increment after reset', (done) => { + let callbackCount = 0; + + context.counter.subscribe((value) => { + callbackCount++; + if (callbackCount === 1) { + expect(value).to.equal(0); + context.increment(); + } else if (callbackCount === 2) { + expect(value).to.equal(1); + context.reset(); + } else if (callbackCount === 3) { + expect(value).to.equal(0); + context.increment(); + } else if (callbackCount === 4) { + expect(value).to.equal(1); + done(); + } + }); + }); + }); + + describe('Context integration', () => { + it('provides context that can be consumed by other components', () => { + // Verify context is available for consumption + expect(EXAMPLE_COUNTER_CONTEXT).to.not.be.undefined; + }); + }); +}); + +describe('ExampleCounterWorkspaceView', () => { + let element: ExampleCounterWorkspaceView; + let context: WorkspaceContextCounterElement; + let hostElement: UmbLitElement; + + beforeEach(async () => { + hostElement = await fixture(`<${testHostElement}>`); + context = new WorkspaceContextCounterElement(hostElement); + + element = await fixture(html``, { + parentNode: hostElement, + }); + + // Wait for context consumption + await element.updateComplete; + }); + + describe('Counter value display', () => { + it('shows initial counter value', async () => { + await element.updateComplete; + const displayText = element.shadowRoot?.textContent; + expect(displayText).to.include('Current count value: 0'); + }); + + it('reflects counter value changes when incremented', async () => { + context.increment(); + await element.updateComplete; + + const displayText = element.shadowRoot?.textContent; + expect(displayText).to.include('Current count value: 1'); + }); + + it('reflects counter value changes when incremented multiple times', async () => { + context.increment(); + context.increment(); + context.increment(); + await element.updateComplete; + + const displayText = element.shadowRoot?.textContent; + expect(displayText).to.include('Current count value: 3'); + }); + + it('reflects counter value changes when reset', async () => { + context.increment(); + context.increment(); + await element.updateComplete; + + let displayText = element.shadowRoot?.textContent; + expect(displayText).to.include('Current count value: 2'); + + context.reset(); + await element.updateComplete; + + displayText = element.shadowRoot?.textContent; + expect(displayText).to.include('Current count value: 0'); + }); + + it('maintains correct value display through increment and reset cycles', async () => { + // Test a complete workflow cycle + context.increment(); + context.increment(); + context.increment(); + await element.updateComplete; + expect(element.shadowRoot?.textContent).to.include('Current count value: 3'); + + context.reset(); + await element.updateComplete; + expect(element.shadowRoot?.textContent).to.include('Current count value: 0'); + + context.increment(); + await element.updateComplete; + expect(element.shadowRoot?.textContent).to.include('Current count value: 1'); + }); + }); +}); + +describe('ExampleCounterStatusFooterAppElement', () => { + let element: ExampleCounterStatusFooterAppElement; + let context: WorkspaceContextCounterElement; + let hostElement: UmbLitElement; + + beforeEach(async () => { + hostElement = await fixture(`<${testHostElement}>`); + context = new WorkspaceContextCounterElement(hostElement); + + element = await fixture(html``, { + parentNode: hostElement, + }); + + // Wait for context consumption + await element.updateComplete; + }); + + describe('Status display', () => { + it('shows initial counter status', async () => { + await element.updateComplete; + const displayText = element.shadowRoot?.textContent; + expect(displayText).to.include('Counter: 0'); + }); + + it('reflects counter state changes when incremented', async () => { + context.increment(); + await element.updateComplete; + + const displayText = element.shadowRoot?.textContent; + expect(displayText).to.include('Counter: 1'); + }); + + it('reflects counter state changes when incremented multiple times', async () => { + context.increment(); + context.increment(); + context.increment(); + context.increment(); + context.increment(); + await element.updateComplete; + + const displayText = element.shadowRoot?.textContent; + expect(displayText).to.include('Counter: 5'); + }); + + it('reflects counter state changes when reset', async () => { + context.increment(); + context.increment(); + context.increment(); + await element.updateComplete; + + let displayText = element.shadowRoot?.textContent; + expect(displayText).to.include('Counter: 3'); + + context.reset(); + await element.updateComplete; + + displayText = element.shadowRoot?.textContent; + expect(displayText).to.include('Counter: 0'); + }); + + it('maintains accurate status display through complete workflow cycles', async () => { + // Test comprehensive state change tracking + await element.updateComplete; + expect(element.shadowRoot?.textContent).to.include('Counter: 0'); + + context.increment(); + context.increment(); + await element.updateComplete; + expect(element.shadowRoot?.textContent).to.include('Counter: 2'); + + context.reset(); + await element.updateComplete; + expect(element.shadowRoot?.textContent).to.include('Counter: 0'); + + context.increment(); + await element.updateComplete; + expect(element.shadowRoot?.textContent).to.include('Counter: 1'); + }); + + it('synchronizes with context state changes across multiple increments and resets', async () => { + // Test synchronization with rapid state changes + context.increment(); + context.increment(); + context.increment(); + context.reset(); + context.increment(); + context.increment(); + await element.updateComplete; + + const displayText = element.shadowRoot?.textContent; + expect(displayText).to.include('Counter: 2'); + }); + }); +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index bb6582d6d2..640d3feac8 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -191,6 +191,10 @@ "test:dev-watch": "npm run generate:check-const-test && web-test-runner --watch --config ./web-test-runner.dev.config.mjs", "test:watch": "npm run generate:check-const-test && web-test-runner --watch", "test": "npm run generate:check-const-test && npm run check:module-dependencies && web-test-runner", + "test:examples": "npm run generate:check-const-test && web-test-runner --files \"./examples/**/*.test.ts\"", + "test:examples:dev": "npm run generate:check-const-test && web-test-runner --config ./web-test-runner.dev.config.mjs --files \"./examples/**/*.test.ts\"", + "test:examples:watch": "npm run generate:check-const-test && web-test-runner --watch --config ./web-test-runner.dev.config.mjs --files \"./examples/**/*.test.ts\"", + "test:examples:browser": "npm run generate:check-const-test && web-test-runner --config ./web-test-runner.dev.config.mjs --files \"./examples/**/*.test.ts\" --manual", "wc-analyze:vscode": "wca **/*.element.ts --format vscode --outFile dist-cms/vscode-html-custom-data.json", "wc-analyze": "wca **/*.element.ts --outFile dist-cms/custom-elements.json", "generate:tsconfig": "node ./devops/tsconfig/index.js",