Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ jobs:
run: npm install && npm run typecheck && npm test
working-directory: examples/testing/point-of-sale-testing-example

- name: Testing example (customer-account)
run: npm install && npm run typecheck && npm test
working-directory: examples/testing/customer-account-testing-example

test-build:
runs-on: ubuntu-latest

Expand Down
29 changes: 29 additions & 0 deletions examples/testing/customer-account-testing-example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Environment Configuration
.env
.env.*

# Dependency directory
node_modules

# Test coverage directory
coverage

# Ignore Apple macOS Desktop Services Store
.DS_Store

# Logs
logs
*.log

# extensions build output
extensions/*/build
extensions/*/dist

# lock files




# Ignore shopify files created during app dev
.shopify/*
.shopify.lock
30 changes: 30 additions & 0 deletions examples/testing/customer-account-testing-example/.graphqlrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const fs = require("node:fs");

function getConfig() {
const config = {
projects: {},
};

let extensions = [];
try {
extensions = fs.readdirSync("./extensions");
} catch {
// ignore if no extensions
}

for (const entry of extensions) {
const extensionPath = `./extensions/${entry}`;
const schema = `${extensionPath}/schema.graphql`;
if (!fs.existsSync(schema)) {
continue;
}
config.projects[entry] = {
schema,
documents: [`${extensionPath}/**/*.graphql`],
};
}

return config;
}

module.exports = getConfig();
24 changes: 24 additions & 0 deletions examples/testing/customer-account-testing-example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
This extension was created with:

```
shopify app init --name customer-account-testing-example
cd customer-account-testing-example
shopify app generate extension
# I chose 'Customer account UI'
```

See it in action:

Build the root package:

```
yarn build
```

Change into this example package and run:

```
npm install
npm run typecheck
npm test
```
Empty file.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "customer-account-testing-example-ext",
"private": true,
"version": "1.0.0",
"license": "UNLICENSED",
"dependencies": {
"preact": "^10.10.x",
"@preact/signals": "^2.3.x",
"@shopify/ui-extensions": "file:../../../../packages/ui-extensions"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import '@shopify/ui-extensions';

//@ts-ignore
declare module './src/OrderStatusBlock.jsx' {
const shopify: import('@shopify/ui-extensions/customer-account.order-status.block.render').Api;
const globalThis: { shopify: typeof shopify };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Learn more about configuring your Customer account UI extension:
# https://shopify.dev/api/customer-account-ui-extensions/unstable/configuration

# The version of APIs your extension will receive. Learn more:
# https://shopify.dev/docs/api/usage/versioning
api_version = "2026-04"

[[extensions]]
name = "customer-account-testing-example"
handle = "customer-account-testing-example"
type = "ui_extension"
uid = "9ebaa5f0-a0d8-25bf-6290-c55f579e4dfac7c7fcd2"

[[extensions.targeting]]
module = "./src/OrderStatusBlock.jsx"
target = "customer-account.order-status.block.render"

[extensions.capabilities]
api_access = true
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import '@shopify/ui-extensions/preact';
import {render} from 'preact';

export default async () => {
render(<Extension />, document.body);
};

function Extension() {
const order = shopify.order.value;
const totalAmount = shopify.cost.totalAmount.value;

return (
<s-banner>
<s-heading>{order ? `Order ${order.name}` : 'Order Summary'}</s-heading>
<s-text>
Total: {totalAmount.amount} {totalAmount.currencyCode}
</s-text>
</s-banner>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/// <reference types="@shopify/ui-extensions/customer-account.order-status.block.render" />
// ^ This defines types for custom Shopify elements supported by the target.

import {expect, test, beforeEach, afterEach} from 'vitest';
import {getExtension} from '@shopify/ui-extensions-tester';

const extension = getExtension(
'customer-account.order-status.block.render',
);

beforeEach(() => {
extension.setUp();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: why not setup?

Copy link
Contributor Author

@kumar303 kumar303 Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because that's not grammatically correct? πŸ˜… The imperative verb tense is set up and in noun form it would be setup. Forgive me for being pedantic, sorry; I just need the illusion of control in my life sometimes.

});

afterEach(() => {
extension.tearDown();
});

test('shows a generic heading when there is no order', async () => {
extension.shopify.order.value = undefined;

await extension.render();

const heading = document.body.querySelector('s-heading')!;
expect(heading.textContent).toEqual('Order Summary');
});

test('shows the order name in the heading', async () => {
extension.shopify.order.value!.name = '#1042';

await extension.render();

const heading = document.body.querySelector('s-heading')!;
expect(heading.textContent).toEqual('Order #1042');
});

test('shows the total amount with currency', async () => {
await extension.render();

const texts = Array.from(document.body.querySelectorAll('s-text'));
const content = texts.map((el) => el.textContent).join('');
expect(content).toContain('0');
expect(content).toContain('USD');
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "preact",
"target": "ES2020",
"allowJs": true,
"moduleResolution": "node",
"esModuleInterop": true,
"noEmit": true,
"strict": true,
"skipLibCheck": true,
// Monorepo only: file: deps create symlinks that cause TS to
// resolve a different preact instance. Not needed with published packages.
"paths": {
"preact": ["../../node_modules/preact"],
"preact/*": ["../../node_modules/preact/*"]
}
},
"include": ["./src", "./tests", "./shopify.d.ts"]
}
Loading
Loading