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}>${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}>${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}>${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",