From 81d906110d65cde6467a0b4be50031be4f508a39 Mon Sep 17 00:00:00 2001 From: Richard Powell Date: Wed, 30 Sep 2026 10:19:07 -0400 Subject: [PATCH] Add end-to-end tests for app info --- .nxignore | 2 + .prettierignore | 2 + eslint.config.js | 2 +- package.json | 9 +- test/README.md | 103 + test/app-info.test.ts | 3715 +++++++++++++++++ test/fixtures/.gitignore | 3 + test/fixtures/app-information/README.md | 55 + .../client-id-override/project/package.json | 1 + .../client-id-override/project/pnpm-lock.yaml | 1 + .../project/shopify.app.other.toml | 10 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.remote-fixture.toml | 10 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../dotenv-directory/project/.env/.gitkeep | 0 .../dotenv-directory/project/package.json | 1 + .../dotenv-directory/project/pnpm-lock.yaml | 1 + .../dotenv-directory/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../dotenv-parsing/project/.env | 4 + .../dotenv-parsing/project/package.json | 1 + .../dotenv-parsing/project/pnpm-lock.yaml | 1 + .../dotenv-parsing/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../dotenv-secret/project/.env | 1 + .../dotenv-secret/project/package.json | 1 + .../dotenv-secret/project/pnpm-lock.yaml | 1 + .../dotenv-secret/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/.env | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../dotenv-web-secret/project/.env | 1 + .../dotenv-web-secret/project/package.json | 1 + .../dotenv-web-secret/project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/another/shopify.web.toml | 3 + .../duplicate-web-roles/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/.shopify/project.json | 1 + .../empty-dev-store/project/package.json | 1 + .../empty-dev-store/project/pnpm-lock.yaml | 1 + .../empty-dev-store/project/shopify.app.toml | 13 + .../project/web/shopify.web.toml | 4 + .../empty-web-config/project/package.json | 1 + .../empty-web-config/project/pnpm-lock.yaml | 1 + .../empty-web-config/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 0 .../environments-invalid/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/shopify.environments.toml | 1 + .../project/web/shopify.web.toml | 4 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/shopify.environments.toml | 0 .../project/web/shopify.web.toml | 4 + .../shopify.environments.toml | 1 + .../extensions/fixture/locales/fr.json | 1 + .../extensions/fixture/shopify.extension.toml | 6 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../extensions/fixture/locales/fr.json | 1 + .../extensions/fixture/shopify.extension.toml | 9 + .../function-extension/project/package.json | 1 + .../function-extension/project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/.shopify/project.json | 1 + .../hidden-array/project/package.json | 1 + .../hidden-array/project/pnpm-lock.yaml | 1 + .../hidden-array/project/shopify.app.toml | 10 + .../hidden-array/project/web/shopify.web.toml | 4 + .../project/.shopify/project.json | 1 + .../hidden-boolean/project/package.json | 1 + .../hidden-boolean/project/pnpm-lock.yaml | 1 + .../hidden-boolean/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/.shopify | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/.shopify/project.json | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/.shopify/project.json | 1 + .../hidden-invalid-json/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../hidden-legacy/project/.shopify/.gitignore | 1 + .../project/.shopify/project.json | 1 + .../hidden-legacy/project/package.json | 1 + .../hidden-legacy/project/pnpm-lock.yaml | 1 + .../hidden-legacy/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../hidden-null/project/.shopify/project.json | 1 + .../hidden-null/project/package.json | 1 + .../hidden-null/project/pnpm-lock.yaml | 1 + .../hidden-null/project/shopify.app.toml | 10 + .../hidden-null/project/web/shopify.web.toml | 4 + .../project/.shopify/project.json | 1 + .../hidden-number/project/package.json | 1 + .../hidden-number/project/pnpm-lock.yaml | 1 + .../hidden-number/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../hidden-symlink/project/.shopify/.gitkeep | 0 .../project/.shopify/project.json | 1 + .../hidden-symlink/project/hidden-target.json | 1 + .../hidden-symlink/project/package.json | 1 + .../hidden-symlink/project/pnpm-lock.yaml | 1 + .../hidden-symlink/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/.shopify/copied/shopify.web.toml | 3 + .../node_modules/dependency/shopify.web.toml | 1 + .../hidden-web-discovery/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../inactive-locales/project/locales/fr.json | 0 .../inactive-locales/project/package.json | 1 + .../inactive-locales/project/pnpm-lock.yaml | 1 + .../inactive-locales/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/other/shopify.web.toml | 3 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.inactive.toml | 11 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 1 + .../project/web/shopify.web.toml | 4 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 1 + .../project/web/shopify.web.toml | 4 + .../invalid-configs-help/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 1 + .../project/shopify.environments.toml | 1 + .../project/web/shopify.web.toml | 4 + .../legacy-web-type/project/package.json | 1 + .../legacy-web-type/project/pnpm-lock.yaml | 1 + .../legacy-web-type/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 3 + .../linked-app-dev-store/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 14 + .../project/web/shopify.web.toml | 4 + .../linked-app/project/package.json | 1 + .../linked-app/project/pnpm-lock.yaml | 1 + .../linked-app/project/shopify.app.toml | 10 + .../linked-app/project/web/shopify.web.toml | 4 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 18 + .../project/web/shopify.web.toml | 4 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 17 + .../project/web/shopify.web.toml | 4 + .../linked-mixed-privacy/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 18 + .../project/web/shopify.web.toml | 4 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 18 + .../project/web/shopify.web.toml | 4 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 19 + .../project/web/shopify.web.toml | 4 + .../linked-uri-shorthand/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 15 + .../project/web/shopify.web.toml | 4 + .../local-cli-dependency/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/locales/en.default.json | 0 .../locales-empty/project/package.json | 1 + .../locales-empty/project/pnpm-lock.yaml | 1 + .../locales-empty/project/shopify.app.toml | 13 + .../project/web/shopify.web.toml | 4 + .../project/locales/en.default.json | 1 + .../locales-invalid-utf8/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 13 + .../project/web/shopify.web.toml | 4 + .../project/locales/en.default.json | 1 + .../project/locales/fr.default.json | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 13 + .../project/web/shopify.web.toml | 4 + .../project/locales/fr.json | 1 + .../locales-no-default/project/package.json | 1 + .../locales-no-default/project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 13 + .../project/web/shopify.web.toml | 4 + .../project/locales/en.default.json | 1 + .../locales-not-json/project/package.json | 1 + .../locales-not-json/project/pnpm-lock.yaml | 1 + .../locales-not-json/project/shopify.app.toml | 13 + .../project/web/shopify.web.toml | 4 + .../project/locales/en.default.json | 1 + .../locales-valid/project/package.json | 1 + .../locales-valid/project/pnpm-lock.yaml | 1 + .../locales-valid/project/shopify.app.toml | 13 + .../project/web/shopify.web.toml | 4 + .../extensions/not-discovered/locales/fr.json | 0 .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 13 + .../project/web/shopify.web.toml | 4 + .../localized-module/project/package.json | 1 + .../localized-module/project/pnpm-lock.yaml | 1 + .../localized-module/project/shopify.app.toml | 13 + .../project/web/shopify.web.toml | 4 + .../manifest-bom/project/package.json | 1 + .../manifest-bom/project/pnpm-lock.yaml | 1 + .../manifest-bom/project/shopify.app.toml | 10 + .../manifest-bom/project/web/shopify.web.toml | 4 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../manifest-null/project/package.json | 1 + .../manifest-null/project/pnpm-lock.yaml | 1 + .../manifest-null/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../missing-manifest/project/pnpm-lock.yaml | 1 + .../missing-manifest/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../named-config-dotenv/project/.env | 2 + .../named-config-dotenv/project/.env.staging | 1 + .../named-config-dotenv/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.staging.toml | 10 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../named-config-no-dotenv/project/.env | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.staging.toml | 10 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../named-config-only/project/package.json | 1 + .../named-config-only/project/pnpm-lock.yaml | 1 + .../project/shopify.app.staging.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.staging.toml | 10 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../named-staging/project/package.json | 1 + .../named-staging/project/pnpm-lock.yaml | 1 + .../project/shopify.app.staging.toml | 10 + .../named-staging/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/package.json | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../no-project/project/.gitkeep | 0 .../no-web/project/package.json | 1 + .../no-web/project/pnpm-lock.yaml | 1 + .../no-web/project/shopify.app.toml | 10 + .../package-manager-bun/project/bun.lock | 1 + .../package-manager-bun/project/package.json | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/package-lock.json | 1 + .../package-manager-npm/project/package.json | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../package-manager-pnpm/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../package-manager-yarn/project/package.json | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../package-manager-yarn/project/yarn.lock | 1 + .../project/package-lock.json | 1 + .../project/package.json | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../package-marker-ancestor/yarn.lock | 0 .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 15 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../report-components/project/package.json | 1 + .../report-components/project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../template-current/project/package.json | 1 + .../template-current/project/pnpm-lock.yaml | 1 + .../template-current/project/shopify.app.toml | 3 + .../project/web/shopify.web.toml | 4 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 2 + .../project/web/shopify.web.toml | 4 + .../template-name-only/project/package.json | 1 + .../template-name-only/project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 1 + .../project/web/shopify.web.toml | 4 + .../template-root-scopes/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 1 + .../project/web/shopify.web.toml | 4 + .../extensions/fixture/locales/fr.json | 1 + .../extensions/fixture/shopify.extension.toml | 5 + .../theme-extension/project/package.json | 1 + .../theme-extension/project/pnpm-lock.yaml | 1 + .../theme-extension/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../extensions/ui/shopify.extension.toml | 9 + .../project/extensions/ui/src/index.ts | 1 + .../ui-ancestor-tsconfig/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/tsconfig.json | 1 + .../project/web/shopify.web.toml | 4 + .../project/extensions/ui/intent.json | 1 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 17 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../ui-duplicate-intents/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 8 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../ui-errors-no-uid/project/package.json | 1 + .../ui-errors-no-uid/project/pnpm-lock.yaml | 1 + .../ui-errors-no-uid/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 0 .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 9 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../ui-extension/project/package.json | 1 + .../ui-extension/project/pnpm-lock.yaml | 1 + .../ui-extension/project/shopify.app.toml | 10 + .../ui-extension/project/web/shopify.web.toml | 4 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 10 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tools.json | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 10 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tools.json | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 9 + .../project/extensions/ui/src/helper.ts | 1 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../ui-imported-source/project/package.json | 1 + .../ui-imported-source/project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/extensions/ui/intent.json | 1 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 13 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/extensions/ui/intent.json | 1 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 13 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 13 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/extensions/ui/intent.json | 1 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 13 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../ui-intent-valid/project/package.json | 1 + .../ui-intent-valid/project/pnpm-lock.yaml | 1 + .../ui-intent-valid/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 10 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tools.json | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../project/input.json | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 10 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tools.json | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../extensions/ui/shopify.extension.toml | 9 + .../project/extensions/ui/tsconfig.json | 1 + .../ui-missing-source/project/package.json | 1 + .../ui-missing-source/project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 9 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../extensions/ui/shopify.extension.toml | 9 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/extensions/ui/shopify.d.ts | 1 + .../extensions/ui/shopify.extension.toml | 14 + .../project/extensions/ui/src/index.ts | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 8 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../ui-no-uid/project/package.json | 1 + .../ui-no-uid/project/pnpm-lock.yaml | 1 + .../ui-no-uid/project/shopify.app.toml | 10 + .../ui-no-uid/project/web/shopify.web.toml | 4 + .../project/extensions/ui/shopify.d.ts | 1 + .../extensions/ui/shopify.extension.toml | 14 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.d.ts/.gitkeep | 0 .../extensions/ui/shopify.extension.toml | 9 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../ui-output-directory/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../@shopify/ui-extensions/globals.d.ts | 1 + .../@shopify/ui-extensions/index.d.ts | 2 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 9 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../ui-shopify-global/project/package.json | 1 + .../ui-shopify-global/project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 10 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tools.json | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 10 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tools.json | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../ui-tools-empty/project/package.json | 1 + .../ui-tools-empty/project/pnpm-lock.yaml | 1 + .../ui-tools-empty/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 10 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tools.json | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 10 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tools.json | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 10 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 10 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tools.json | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../ui-tools-valid/project/package.json | 1 + .../ui-tools-valid/project/pnpm-lock.yaml | 1 + .../ui-tools-valid/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../@shopify/ui-extensions/index.d.ts | 1 + .../@shopify/ui-extensions/package.json | 1 + .../extensions/ui/shopify.extension.toml | 10 + .../project/extensions/ui/src/index.ts | 1 + .../project/extensions/ui/tools.json | 1 + .../project/extensions/ui/tsconfig.json | 1 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../unlinked-client-id/project/package.json | 1 + .../unlinked-client-id/project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 1 + .../project/web/shopify.web.toml | 4 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 1 + .../project/web/shopify.web.toml | 4 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../web-default-frontend/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 2 + .../web-empty-roles/project/package.json | 1 + .../web-empty-roles/project/pnpm-lock.yaml | 1 + .../web-empty-roles/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 5 + .../web-invalid-command/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 3 + .../project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 5 + .../web-missing-command/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 1 + .../web-port-fraction/project/package.json | 1 + .../web-port-fraction/project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 3 + .../web-port-negative/project/package.json | 1 + .../web-port-negative/project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 3 + .../web-port-string/project/package.json | 1 + .../web-port-string/project/pnpm-lock.yaml | 1 + .../web-port-string/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 3 + .../web-port-too-large/project/package.json | 1 + .../web-port-too-large/project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 3 + .../web-port-zero/project/package.json | 1 + .../web-port-zero/project/pnpm-lock.yaml | 1 + .../web-port-zero/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 3 + .../web-remix-framework/project/package.json | 1 + .../project/pnpm-lock.yaml | 1 + .../project/shopify.app.toml | 10 + .../project/web/package.json | 1 + .../project/web/shopify.web.toml | 4 + .../web-roles-win/project/package.json | 1 + .../web-roles-win/project/pnpm-lock.yaml | 1 + .../web-roles-win/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 12 + .../workspace-marker/project/package.json | 1 + .../workspace-marker/project/pnpm-lock.yaml | 1 + .../project/pnpm-workspace.yaml | 1 + .../workspace-marker/project/shopify.app.toml | 10 + .../project/web/shopify.web.toml | 4 + test/support/build.ts | 66 + test/support/filesystem.ts | 98 + test/support/fixture.ts | 510 +++ test/support/loopback.ts | 25 + test/support/observations.ts | 35 + test/support/preload.ts | 146 + test/support/process.ts | 166 + test/support/protocol.ts | 53 + test/support/scenarios.ts | 225 + test/support/state.ts | 74 + test/tsconfig.json | 14 + test/vite.config.ts | 17 + tsconfig.json | 2 +- vite.config.ts | 1 + 687 files changed, 7712 insertions(+), 5 deletions(-) create mode 100644 test/README.md create mode 100644 test/app-info.test.ts create mode 100644 test/fixtures/.gitignore create mode 100644 test/fixtures/app-information/README.md create mode 100644 test/fixtures/app-information/client-id-override/project/package.json create mode 100644 test/fixtures/app-information/client-id-override/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/client-id-override/project/shopify.app.other.toml create mode 100644 test/fixtures/app-information/client-id-override/project/shopify.app.toml create mode 100644 test/fixtures/app-information/client-id-override/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/config-name-collision/project/package.json create mode 100644 test/fixtures/app-information/config-name-collision/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/config-name-collision/project/shopify.app.remote-fixture.toml create mode 100644 test/fixtures/app-information/config-name-collision/project/shopify.app.toml create mode 100644 test/fixtures/app-information/config-name-collision/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/dotenv-directory/project/.env/.gitkeep create mode 100644 test/fixtures/app-information/dotenv-directory/project/package.json create mode 100644 test/fixtures/app-information/dotenv-directory/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/dotenv-directory/project/shopify.app.toml create mode 100644 test/fixtures/app-information/dotenv-directory/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/dotenv-parsing/project/.env create mode 100644 test/fixtures/app-information/dotenv-parsing/project/package.json create mode 100644 test/fixtures/app-information/dotenv-parsing/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/dotenv-parsing/project/shopify.app.toml create mode 100644 test/fixtures/app-information/dotenv-parsing/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/dotenv-secret/project/.env create mode 100644 test/fixtures/app-information/dotenv-secret/project/package.json create mode 100644 test/fixtures/app-information/dotenv-secret/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/dotenv-secret/project/shopify.app.toml create mode 100644 test/fixtures/app-information/dotenv-secret/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/dotenv-unused-during-parse-failure/project/.env create mode 100644 test/fixtures/app-information/dotenv-unused-during-parse-failure/project/package.json create mode 100644 test/fixtures/app-information/dotenv-unused-during-parse-failure/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/dotenv-unused-during-parse-failure/project/shopify.app.toml create mode 100644 test/fixtures/app-information/dotenv-unused-during-parse-failure/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/dotenv-web-secret/project/.env create mode 100644 test/fixtures/app-information/dotenv-web-secret/project/package.json create mode 100644 test/fixtures/app-information/dotenv-web-secret/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/dotenv-web-secret/project/shopify.app.toml create mode 100644 test/fixtures/app-information/dotenv-web-secret/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/duplicate-web-roles/project/another/shopify.web.toml create mode 100644 test/fixtures/app-information/duplicate-web-roles/project/package.json create mode 100644 test/fixtures/app-information/duplicate-web-roles/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/duplicate-web-roles/project/shopify.app.toml create mode 100644 test/fixtures/app-information/duplicate-web-roles/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/empty-dev-store/project/.shopify/project.json create mode 100644 test/fixtures/app-information/empty-dev-store/project/package.json create mode 100644 test/fixtures/app-information/empty-dev-store/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/empty-dev-store/project/shopify.app.toml create mode 100644 test/fixtures/app-information/empty-dev-store/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/empty-web-config/project/package.json create mode 100644 test/fixtures/app-information/empty-web-config/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/empty-web-config/project/shopify.app.toml create mode 100644 test/fixtures/app-information/empty-web-config/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/environments-invalid/project/package.json create mode 100644 test/fixtures/app-information/environments-invalid/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/environments-invalid/project/shopify.app.toml create mode 100644 test/fixtures/app-information/environments-invalid/project/shopify.environments.toml create mode 100644 test/fixtures/app-information/environments-invalid/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/environments-shadowing/project/package.json create mode 100644 test/fixtures/app-information/environments-shadowing/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/environments-shadowing/project/shopify.app.toml create mode 100644 test/fixtures/app-information/environments-shadowing/project/shopify.environments.toml create mode 100644 test/fixtures/app-information/environments-shadowing/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/environments-shadowing/shopify.environments.toml create mode 100644 test/fixtures/app-information/flow-action-extension/project/extensions/fixture/locales/fr.json create mode 100644 test/fixtures/app-information/flow-action-extension/project/extensions/fixture/shopify.extension.toml create mode 100644 test/fixtures/app-information/flow-action-extension/project/package.json create mode 100644 test/fixtures/app-information/flow-action-extension/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/flow-action-extension/project/shopify.app.toml create mode 100644 test/fixtures/app-information/flow-action-extension/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/function-extension/project/extensions/fixture/locales/fr.json create mode 100644 test/fixtures/app-information/function-extension/project/extensions/fixture/shopify.extension.toml create mode 100644 test/fixtures/app-information/function-extension/project/package.json create mode 100644 test/fixtures/app-information/function-extension/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/function-extension/project/shopify.app.toml create mode 100644 test/fixtures/app-information/function-extension/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/hidden-array/project/.shopify/project.json create mode 100644 test/fixtures/app-information/hidden-array/project/package.json create mode 100644 test/fixtures/app-information/hidden-array/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/hidden-array/project/shopify.app.toml create mode 100644 test/fixtures/app-information/hidden-array/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/hidden-boolean/project/.shopify/project.json create mode 100644 test/fixtures/app-information/hidden-boolean/project/package.json create mode 100644 test/fixtures/app-information/hidden-boolean/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/hidden-boolean/project/shopify.app.toml create mode 100644 test/fixtures/app-information/hidden-boolean/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/hidden-directory-collision/project/.shopify create mode 100644 test/fixtures/app-information/hidden-directory-collision/project/package.json create mode 100644 test/fixtures/app-information/hidden-directory-collision/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/hidden-directory-collision/project/shopify.app.toml create mode 100644 test/fixtures/app-information/hidden-directory-collision/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/hidden-empty-matching-entry/project/.shopify/project.json create mode 100644 test/fixtures/app-information/hidden-empty-matching-entry/project/package.json create mode 100644 test/fixtures/app-information/hidden-empty-matching-entry/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/hidden-empty-matching-entry/project/shopify.app.toml create mode 100644 test/fixtures/app-information/hidden-empty-matching-entry/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/hidden-invalid-json/project/.shopify/project.json create mode 100644 test/fixtures/app-information/hidden-invalid-json/project/package.json create mode 100644 test/fixtures/app-information/hidden-invalid-json/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/hidden-invalid-json/project/shopify.app.toml create mode 100644 test/fixtures/app-information/hidden-invalid-json/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/hidden-legacy/project/.shopify/.gitignore create mode 100644 test/fixtures/app-information/hidden-legacy/project/.shopify/project.json create mode 100644 test/fixtures/app-information/hidden-legacy/project/package.json create mode 100644 test/fixtures/app-information/hidden-legacy/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/hidden-legacy/project/shopify.app.toml create mode 100644 test/fixtures/app-information/hidden-legacy/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/hidden-null/project/.shopify/project.json create mode 100644 test/fixtures/app-information/hidden-null/project/package.json create mode 100644 test/fixtures/app-information/hidden-null/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/hidden-null/project/shopify.app.toml create mode 100644 test/fixtures/app-information/hidden-null/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/hidden-number/project/.shopify/project.json create mode 100644 test/fixtures/app-information/hidden-number/project/package.json create mode 100644 test/fixtures/app-information/hidden-number/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/hidden-number/project/shopify.app.toml create mode 100644 test/fixtures/app-information/hidden-number/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/hidden-symlink/project/.shopify/.gitkeep create mode 120000 test/fixtures/app-information/hidden-symlink/project/.shopify/project.json create mode 100644 test/fixtures/app-information/hidden-symlink/project/hidden-target.json create mode 100644 test/fixtures/app-information/hidden-symlink/project/package.json create mode 100644 test/fixtures/app-information/hidden-symlink/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/hidden-symlink/project/shopify.app.toml create mode 100644 test/fixtures/app-information/hidden-symlink/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/hidden-web-discovery/project/.shopify/copied/shopify.web.toml create mode 100644 test/fixtures/app-information/hidden-web-discovery/project/node_modules/dependency/shopify.web.toml create mode 100644 test/fixtures/app-information/hidden-web-discovery/project/package.json create mode 100644 test/fixtures/app-information/hidden-web-discovery/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/hidden-web-discovery/project/shopify.app.toml create mode 100644 test/fixtures/app-information/hidden-web-discovery/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/inactive-locales/project/locales/fr.json create mode 100644 test/fixtures/app-information/inactive-locales/project/package.json create mode 100644 test/fixtures/app-information/inactive-locales/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/inactive-locales/project/shopify.app.toml create mode 100644 test/fixtures/app-information/inactive-locales/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/inactive-web-directories/project/other/shopify.web.toml create mode 100644 test/fixtures/app-information/inactive-web-directories/project/package.json create mode 100644 test/fixtures/app-information/inactive-web-directories/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/inactive-web-directories/project/shopify.app.inactive.toml create mode 100644 test/fixtures/app-information/inactive-web-directories/project/shopify.app.toml create mode 100644 test/fixtures/app-information/inactive-web-directories/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/invalid-app-client-id-type/project/package.json create mode 100644 test/fixtures/app-information/invalid-app-client-id-type/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/invalid-app-client-id-type/project/shopify.app.toml create mode 100644 test/fixtures/app-information/invalid-app-client-id-type/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/invalid-app-toml-syntax/project/package.json create mode 100644 test/fixtures/app-information/invalid-app-toml-syntax/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/invalid-app-toml-syntax/project/shopify.app.toml create mode 100644 test/fixtures/app-information/invalid-app-toml-syntax/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/invalid-configs-help/project/package.json create mode 100644 test/fixtures/app-information/invalid-configs-help/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/invalid-configs-help/project/shopify.app.toml create mode 100644 test/fixtures/app-information/invalid-configs-help/project/shopify.environments.toml create mode 100644 test/fixtures/app-information/invalid-configs-help/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/legacy-web-type/project/package.json create mode 100644 test/fixtures/app-information/legacy-web-type/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/legacy-web-type/project/shopify.app.toml create mode 100644 test/fixtures/app-information/legacy-web-type/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/linked-app-dev-store/project/package.json create mode 100644 test/fixtures/app-information/linked-app-dev-store/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/linked-app-dev-store/project/shopify.app.toml create mode 100644 test/fixtures/app-information/linked-app-dev-store/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/linked-app/project/package.json create mode 100644 test/fixtures/app-information/linked-app/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/linked-app/project/shopify.app.toml create mode 100644 test/fixtures/app-information/linked-app/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/linked-endpoint-webhooks/project/package.json create mode 100644 test/fixtures/app-information/linked-endpoint-webhooks/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/linked-endpoint-webhooks/project/shopify.app.toml create mode 100644 test/fixtures/app-information/linked-endpoint-webhooks/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/linked-legacy-privacy/project/package.json create mode 100644 test/fixtures/app-information/linked-legacy-privacy/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/linked-legacy-privacy/project/shopify.app.toml create mode 100644 test/fixtures/app-information/linked-legacy-privacy/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/linked-mixed-privacy/project/package.json create mode 100644 test/fixtures/app-information/linked-mixed-privacy/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/linked-mixed-privacy/project/shopify.app.toml create mode 100644 test/fixtures/app-information/linked-mixed-privacy/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/linked-old-subscription/project/package.json create mode 100644 test/fixtures/app-information/linked-old-subscription/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/linked-old-subscription/project/shopify.app.toml create mode 100644 test/fixtures/app-information/linked-old-subscription/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/linked-required-scopes/project/package.json create mode 100644 test/fixtures/app-information/linked-required-scopes/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/linked-required-scopes/project/shopify.app.toml create mode 100644 test/fixtures/app-information/linked-required-scopes/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/linked-uri-shorthand/project/package.json create mode 100644 test/fixtures/app-information/linked-uri-shorthand/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/linked-uri-shorthand/project/shopify.app.toml create mode 100644 test/fixtures/app-information/linked-uri-shorthand/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/local-cli-dependency/project/package.json create mode 100644 test/fixtures/app-information/local-cli-dependency/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/local-cli-dependency/project/shopify.app.toml create mode 100644 test/fixtures/app-information/local-cli-dependency/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/locales-empty/project/locales/en.default.json create mode 100644 test/fixtures/app-information/locales-empty/project/package.json create mode 100644 test/fixtures/app-information/locales-empty/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/locales-empty/project/shopify.app.toml create mode 100644 test/fixtures/app-information/locales-empty/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/locales-invalid-utf8/project/locales/en.default.json create mode 100644 test/fixtures/app-information/locales-invalid-utf8/project/package.json create mode 100644 test/fixtures/app-information/locales-invalid-utf8/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/locales-invalid-utf8/project/shopify.app.toml create mode 100644 test/fixtures/app-information/locales-invalid-utf8/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/locales-multiple-defaults/project/locales/en.default.json create mode 100644 test/fixtures/app-information/locales-multiple-defaults/project/locales/fr.default.json create mode 100644 test/fixtures/app-information/locales-multiple-defaults/project/package.json create mode 100644 test/fixtures/app-information/locales-multiple-defaults/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/locales-multiple-defaults/project/shopify.app.toml create mode 100644 test/fixtures/app-information/locales-multiple-defaults/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/locales-no-default/project/locales/fr.json create mode 100644 test/fixtures/app-information/locales-no-default/project/package.json create mode 100644 test/fixtures/app-information/locales-no-default/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/locales-no-default/project/shopify.app.toml create mode 100644 test/fixtures/app-information/locales-no-default/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/locales-not-json/project/locales/en.default.json create mode 100644 test/fixtures/app-information/locales-not-json/project/package.json create mode 100644 test/fixtures/app-information/locales-not-json/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/locales-not-json/project/shopify.app.toml create mode 100644 test/fixtures/app-information/locales-not-json/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/locales-valid/project/locales/en.default.json create mode 100644 test/fixtures/app-information/locales-valid/project/package.json create mode 100644 test/fixtures/app-information/locales-valid/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/locales-valid/project/shopify.app.toml create mode 100644 test/fixtures/app-information/locales-valid/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/localized-module-wrong-locale-location/project/extensions/not-discovered/locales/fr.json create mode 100644 test/fixtures/app-information/localized-module-wrong-locale-location/project/package.json create mode 100644 test/fixtures/app-information/localized-module-wrong-locale-location/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/localized-module-wrong-locale-location/project/shopify.app.toml create mode 100644 test/fixtures/app-information/localized-module-wrong-locale-location/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/localized-module/project/package.json create mode 100644 test/fixtures/app-information/localized-module/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/localized-module/project/shopify.app.toml create mode 100644 test/fixtures/app-information/localized-module/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/manifest-bom/project/package.json create mode 100644 test/fixtures/app-information/manifest-bom/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/manifest-bom/project/shopify.app.toml create mode 100644 test/fixtures/app-information/manifest-bom/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/manifest-dependencies-workspaces/project/package.json create mode 100644 test/fixtures/app-information/manifest-dependencies-workspaces/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/manifest-dependencies-workspaces/project/shopify.app.toml create mode 100644 test/fixtures/app-information/manifest-dependencies-workspaces/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/manifest-invalid-json/project/package.json create mode 100644 test/fixtures/app-information/manifest-invalid-json/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/manifest-invalid-json/project/shopify.app.toml create mode 100644 test/fixtures/app-information/manifest-invalid-json/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/manifest-null/project/package.json create mode 100644 test/fixtures/app-information/manifest-null/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/manifest-null/project/shopify.app.toml create mode 100644 test/fixtures/app-information/manifest-null/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/missing-manifest/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/missing-manifest/project/shopify.app.toml create mode 100644 test/fixtures/app-information/missing-manifest/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/named-config-dotenv/project/.env create mode 100644 test/fixtures/app-information/named-config-dotenv/project/.env.staging create mode 100644 test/fixtures/app-information/named-config-dotenv/project/package.json create mode 100644 test/fixtures/app-information/named-config-dotenv/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/named-config-dotenv/project/shopify.app.staging.toml create mode 100644 test/fixtures/app-information/named-config-dotenv/project/shopify.app.toml create mode 100644 test/fixtures/app-information/named-config-dotenv/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/named-config-no-dotenv/project/.env create mode 100644 test/fixtures/app-information/named-config-no-dotenv/project/package.json create mode 100644 test/fixtures/app-information/named-config-no-dotenv/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/named-config-no-dotenv/project/shopify.app.staging.toml create mode 100644 test/fixtures/app-information/named-config-no-dotenv/project/shopify.app.toml create mode 100644 test/fixtures/app-information/named-config-no-dotenv/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/named-config-only/project/package.json create mode 100644 test/fixtures/app-information/named-config-only/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/named-config-only/project/shopify.app.staging.toml create mode 100644 test/fixtures/app-information/named-config-only/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/named-staging-fixture/project/package.json create mode 100644 test/fixtures/app-information/named-staging-fixture/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/named-staging-fixture/project/shopify.app.staging.toml create mode 100644 test/fixtures/app-information/named-staging-fixture/project/shopify.app.toml create mode 100644 test/fixtures/app-information/named-staging-fixture/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/named-staging/project/package.json create mode 100644 test/fixtures/app-information/named-staging/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/named-staging/project/shopify.app.staging.toml create mode 100644 test/fixtures/app-information/named-staging/project/shopify.app.toml create mode 100644 test/fixtures/app-information/named-staging/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/no-package-manager-marker/project/package.json create mode 100644 test/fixtures/app-information/no-package-manager-marker/project/shopify.app.toml create mode 100644 test/fixtures/app-information/no-package-manager-marker/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/no-project/project/.gitkeep create mode 100644 test/fixtures/app-information/no-web/project/package.json create mode 100644 test/fixtures/app-information/no-web/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/no-web/project/shopify.app.toml create mode 100644 test/fixtures/app-information/package-manager-bun/project/bun.lock create mode 100644 test/fixtures/app-information/package-manager-bun/project/package.json create mode 100644 test/fixtures/app-information/package-manager-bun/project/shopify.app.toml create mode 100644 test/fixtures/app-information/package-manager-bun/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/package-manager-npm/project/package-lock.json create mode 100644 test/fixtures/app-information/package-manager-npm/project/package.json create mode 100644 test/fixtures/app-information/package-manager-npm/project/shopify.app.toml create mode 100644 test/fixtures/app-information/package-manager-npm/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/package-manager-pnpm/project/package.json create mode 100644 test/fixtures/app-information/package-manager-pnpm/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/package-manager-pnpm/project/shopify.app.toml create mode 100644 test/fixtures/app-information/package-manager-pnpm/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/package-manager-yarn/project/package.json create mode 100644 test/fixtures/app-information/package-manager-yarn/project/shopify.app.toml create mode 100644 test/fixtures/app-information/package-manager-yarn/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/package-manager-yarn/project/yarn.lock create mode 100644 test/fixtures/app-information/package-marker-ancestor/project/package-lock.json create mode 100644 test/fixtures/app-information/package-marker-ancestor/project/package.json create mode 100644 test/fixtures/app-information/package-marker-ancestor/project/shopify.app.toml create mode 100644 test/fixtures/app-information/package-marker-ancestor/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/package-marker-ancestor/yarn.lock create mode 100644 test/fixtures/app-information/report-components/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/report-components/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/report-components/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/report-components/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/report-components/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/report-components/project/package.json create mode 100644 test/fixtures/app-information/report-components/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/report-components/project/shopify.app.toml create mode 100644 test/fixtures/app-information/report-components/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/template-current/project/package.json create mode 100644 test/fixtures/app-information/template-current/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/template-current/project/shopify.app.toml create mode 100644 test/fixtures/app-information/template-current/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/template-hybrid-client-id/project/package.json create mode 100644 test/fixtures/app-information/template-hybrid-client-id/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/template-hybrid-client-id/project/shopify.app.toml create mode 100644 test/fixtures/app-information/template-hybrid-client-id/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/template-name-only/project/package.json create mode 100644 test/fixtures/app-information/template-name-only/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/template-name-only/project/shopify.app.toml create mode 100644 test/fixtures/app-information/template-name-only/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/template-root-scopes/project/package.json create mode 100644 test/fixtures/app-information/template-root-scopes/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/template-root-scopes/project/shopify.app.toml create mode 100644 test/fixtures/app-information/template-root-scopes/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/theme-extension/project/extensions/fixture/locales/fr.json create mode 100644 test/fixtures/app-information/theme-extension/project/extensions/fixture/shopify.extension.toml create mode 100644 test/fixtures/app-information/theme-extension/project/package.json create mode 100644 test/fixtures/app-information/theme-extension/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/theme-extension/project/shopify.app.toml create mode 100644 test/fixtures/app-information/theme-extension/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-ancestor-tsconfig/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-ancestor-tsconfig/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-ancestor-tsconfig/project/package.json create mode 100644 test/fixtures/app-information/ui-ancestor-tsconfig/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-ancestor-tsconfig/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-ancestor-tsconfig/project/tsconfig.json create mode 100644 test/fixtures/app-information/ui-ancestor-tsconfig/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/intent.json create mode 100644 test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-duplicate-intents/project/package.json create mode 100644 test/fixtures/app-information/ui-duplicate-intents/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-duplicate-intents/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-duplicate-intents/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-errors-no-uid/project/package.json create mode 100644 test/fixtures/app-information/ui-errors-no-uid/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-errors-no-uid/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-errors-no-uid/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-extension/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-extension/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-extension/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-extension/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-extension/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-extension/project/package.json create mode 100644 test/fixtures/app-information/ui-extension/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-extension/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-extension/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/tools.json create mode 100644 test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-http-schema-failure/project/package.json create mode 100644 test/fixtures/app-information/ui-http-schema-failure/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-http-schema-failure/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-http-schema-failure/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/tools.json create mode 100644 test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-http-schema-reference/project/package.json create mode 100644 test/fixtures/app-information/ui-http-schema-reference/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-http-schema-reference/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-http-schema-reference/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-imported-source/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-imported-source/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-imported-source/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-imported-source/project/extensions/ui/src/helper.ts create mode 100644 test/fixtures/app-information/ui-imported-source/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-imported-source/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-imported-source/project/package.json create mode 100644 test/fixtures/app-information/ui-imported-source/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-imported-source/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-imported-source/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/intent.json create mode 100644 test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-intent-invalid-json/project/package.json create mode 100644 test/fixtures/app-information/ui-intent-invalid-json/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-intent-invalid-json/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-intent-invalid-json/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/intent.json create mode 100644 test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-intent-invalid-shape/project/package.json create mode 100644 test/fixtures/app-information/ui-intent-invalid-shape/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-intent-invalid-shape/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-intent-invalid-shape/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-intent-missing-file/project/package.json create mode 100644 test/fixtures/app-information/ui-intent-missing-file/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-intent-missing-file/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-intent-missing-file/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-intent-valid/project/extensions/ui/intent.json create mode 100644 test/fixtures/app-information/ui-intent-valid/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-intent-valid/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-intent-valid/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-intent-valid/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-intent-valid/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-intent-valid/project/package.json create mode 100644 test/fixtures/app-information/ui-intent-valid/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-intent-valid/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-intent-valid/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/tools.json create mode 100644 test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-local-schema-reference/project/input.json create mode 100644 test/fixtures/app-information/ui-local-schema-reference/project/package.json create mode 100644 test/fixtures/app-information/ui-local-schema-reference/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-local-schema-reference/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-local-schema-reference/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/tools.json create mode 100644 test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-missing-helper-export/project/package.json create mode 100644 test/fixtures/app-information/ui-missing-helper-export/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-missing-helper-export/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-missing-helper-export/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-missing-source/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-missing-source/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-missing-source/project/package.json create mode 100644 test/fixtures/app-information/ui-missing-source/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-missing-source/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-missing-source/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-missing-target-export/project/package.json create mode 100644 test/fixtures/app-information/ui-missing-target-export/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-missing-target-export/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-missing-target-export/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-no-installed-package/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-no-installed-package/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-no-installed-package/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-no-installed-package/project/package.json create mode 100644 test/fixtures/app-information/ui-no-installed-package/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-no-installed-package/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-no-installed-package/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-no-tsconfig-stale-types/project/extensions/ui/shopify.d.ts create mode 100644 test/fixtures/app-information/ui-no-tsconfig-stale-types/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-no-tsconfig-stale-types/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-no-tsconfig-stale-types/project/package.json create mode 100644 test/fixtures/app-information/ui-no-tsconfig-stale-types/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-no-tsconfig-stale-types/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-no-tsconfig-stale-types/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-no-uid/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-no-uid/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-no-uid/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-no-uid/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-no-uid/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-no-uid/project/package.json create mode 100644 test/fixtures/app-information/ui-no-uid/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-no-uid/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-no-uid/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-old-api-stale-types/project/extensions/ui/shopify.d.ts create mode 100644 test/fixtures/app-information/ui-old-api-stale-types/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-old-api-stale-types/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-old-api-stale-types/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-old-api-stale-types/project/package.json create mode 100644 test/fixtures/app-information/ui-old-api-stale-types/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-old-api-stale-types/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-old-api-stale-types/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-output-directory/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-output-directory/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-output-directory/project/extensions/ui/shopify.d.ts/.gitkeep create mode 100644 test/fixtures/app-information/ui-output-directory/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-output-directory/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-output-directory/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-output-directory/project/package.json create mode 100644 test/fixtures/app-information/ui-output-directory/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-output-directory/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-output-directory/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-shopify-global/project/extensions/ui/node_modules/@shopify/ui-extensions/globals.d.ts create mode 100644 test/fixtures/app-information/ui-shopify-global/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-shopify-global/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-shopify-global/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-shopify-global/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-shopify-global/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-shopify-global/project/package.json create mode 100644 test/fixtures/app-information/ui-shopify-global/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-shopify-global/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-shopify-global/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/tools.json create mode 100644 test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-tools-duplicate-names/project/package.json create mode 100644 test/fixtures/app-information/ui-tools-duplicate-names/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-tools-duplicate-names/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-tools-duplicate-names/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-tools-empty/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-tools-empty/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-tools-empty/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-tools-empty/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-tools-empty/project/extensions/ui/tools.json create mode 100644 test/fixtures/app-information/ui-tools-empty/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-tools-empty/project/package.json create mode 100644 test/fixtures/app-information/ui-tools-empty/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-tools-empty/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-tools-empty/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/tools.json create mode 100644 test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-tools-invalid-json/project/package.json create mode 100644 test/fixtures/app-information/ui-tools-invalid-json/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-tools-invalid-json/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-tools-invalid-json/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/tools.json create mode 100644 test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-tools-invalid-shape/project/package.json create mode 100644 test/fixtures/app-information/ui-tools-invalid-shape/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-tools-invalid-shape/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-tools-invalid-shape/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-tools-missing-file/project/package.json create mode 100644 test/fixtures/app-information/ui-tools-missing-file/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-tools-missing-file/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-tools-missing-file/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-tools-valid/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-tools-valid/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-tools-valid/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-tools-valid/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-tools-valid/project/extensions/ui/tools.json create mode 100644 test/fixtures/app-information/ui-tools-valid/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-tools-valid/project/package.json create mode 100644 test/fixtures/app-information/ui-tools-valid/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-tools-valid/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-tools-valid/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts create mode 100644 test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json create mode 100644 test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/shopify.extension.toml create mode 100644 test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/src/index.ts create mode 100644 test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/tools.json create mode 100644 test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/tsconfig.json create mode 100644 test/fixtures/app-information/ui-unregistered-schema/project/package.json create mode 100644 test/fixtures/app-information/ui-unregistered-schema/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/ui-unregistered-schema/project/shopify.app.toml create mode 100644 test/fixtures/app-information/ui-unregistered-schema/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/unlinked-client-id/project/package.json create mode 100644 test/fixtures/app-information/unlinked-client-id/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/unlinked-client-id/project/shopify.app.toml create mode 100644 test/fixtures/app-information/unlinked-client-id/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/unlinked-named-template/project/package.json create mode 100644 test/fixtures/app-information/unlinked-named-template/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/unlinked-named-template/project/shopify.app.toml create mode 100644 test/fixtures/app-information/unlinked-named-template/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/web-both-branches-invalid/project/package.json create mode 100644 test/fixtures/app-information/web-both-branches-invalid/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/web-both-branches-invalid/project/shopify.app.toml create mode 100644 test/fixtures/app-information/web-both-branches-invalid/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/web-default-frontend/project/package.json create mode 100644 test/fixtures/app-information/web-default-frontend/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/web-default-frontend/project/shopify.app.toml create mode 100644 test/fixtures/app-information/web-default-frontend/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/web-empty-roles/project/package.json create mode 100644 test/fixtures/app-information/web-empty-roles/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/web-empty-roles/project/shopify.app.toml create mode 100644 test/fixtures/app-information/web-empty-roles/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/web-hmr-missing-paths/project/package.json create mode 100644 test/fixtures/app-information/web-hmr-missing-paths/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/web-hmr-missing-paths/project/shopify.app.toml create mode 100644 test/fixtures/app-information/web-hmr-missing-paths/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/web-invalid-command/project/package.json create mode 100644 test/fixtures/app-information/web-invalid-command/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/web-invalid-command/project/shopify.app.toml create mode 100644 test/fixtures/app-information/web-invalid-command/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/web-invalid-roles-fallback/project/package.json create mode 100644 test/fixtures/app-information/web-invalid-roles-fallback/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/web-invalid-roles-fallback/project/shopify.app.toml create mode 100644 test/fixtures/app-information/web-invalid-roles-fallback/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/web-missing-command/project/package.json create mode 100644 test/fixtures/app-information/web-missing-command/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/web-missing-command/project/shopify.app.toml create mode 100644 test/fixtures/app-information/web-missing-command/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/web-port-fraction/project/package.json create mode 100644 test/fixtures/app-information/web-port-fraction/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/web-port-fraction/project/shopify.app.toml create mode 100644 test/fixtures/app-information/web-port-fraction/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/web-port-negative/project/package.json create mode 100644 test/fixtures/app-information/web-port-negative/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/web-port-negative/project/shopify.app.toml create mode 100644 test/fixtures/app-information/web-port-negative/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/web-port-string/project/package.json create mode 100644 test/fixtures/app-information/web-port-string/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/web-port-string/project/shopify.app.toml create mode 100644 test/fixtures/app-information/web-port-string/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/web-port-too-large/project/package.json create mode 100644 test/fixtures/app-information/web-port-too-large/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/web-port-too-large/project/shopify.app.toml create mode 100644 test/fixtures/app-information/web-port-too-large/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/web-port-zero/project/package.json create mode 100644 test/fixtures/app-information/web-port-zero/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/web-port-zero/project/shopify.app.toml create mode 100644 test/fixtures/app-information/web-port-zero/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/web-remix-framework/project/package.json create mode 100644 test/fixtures/app-information/web-remix-framework/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/web-remix-framework/project/shopify.app.toml create mode 100644 test/fixtures/app-information/web-remix-framework/project/web/package.json create mode 100644 test/fixtures/app-information/web-remix-framework/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/web-roles-win/project/package.json create mode 100644 test/fixtures/app-information/web-roles-win/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/web-roles-win/project/shopify.app.toml create mode 100644 test/fixtures/app-information/web-roles-win/project/web/shopify.web.toml create mode 100644 test/fixtures/app-information/workspace-marker/project/package.json create mode 100644 test/fixtures/app-information/workspace-marker/project/pnpm-lock.yaml create mode 100644 test/fixtures/app-information/workspace-marker/project/pnpm-workspace.yaml create mode 100644 test/fixtures/app-information/workspace-marker/project/shopify.app.toml create mode 100644 test/fixtures/app-information/workspace-marker/project/web/shopify.web.toml create mode 100644 test/support/build.ts create mode 100644 test/support/filesystem.ts create mode 100644 test/support/fixture.ts create mode 100644 test/support/loopback.ts create mode 100644 test/support/observations.ts create mode 100644 test/support/preload.ts create mode 100644 test/support/process.ts create mode 100644 test/support/protocol.ts create mode 100644 test/support/scenarios.ts create mode 100644 test/support/state.ts create mode 100644 test/tsconfig.json create mode 100644 test/vite.config.ts diff --git a/.nxignore b/.nxignore index c750b8d8b7e..b6fd0f39e33 100644 --- a/.nxignore +++ b/.nxignore @@ -1,2 +1,4 @@ **/templates/** docs/** +# Fixture manifests are test input, not Nx projects. +test/fixtures/** diff --git a/.prettierignore b/.prettierignore index 5a9d7463312..eaa28a09e2f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -4,3 +4,5 @@ bin dist *.d.ts ./fixtures/ +# Preserve intentionally invalid JSON/TOML and exact fixture bytes. +test/fixtures/ diff --git a/eslint.config.js b/eslint.config.js index ca4372edc03..4821bc8bf80 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -9,7 +9,7 @@ const config = [ // Global ignores { - ignores: ['**/dist/**', '**/node_modules/**', '**/coverage/**', '**/*.d.ts', '**/*.cjs'], + ignores: ['**/dist/**', '**/node_modules/**', '**/coverage/**', '**/*.d.ts', '**/*.cjs', 'test/fixtures/**'], }, // The local ESLint plugin uses CommonJS so its rules can load themselves. diff --git a/package.json b/package.json index a16442d5847..146ddec192c 100644 --- a/package.json +++ b/package.json @@ -185,7 +185,8 @@ ".": { "entry": [ "bin/**/*.js", - "configurations/vite.config.ts" + "configurations/vite.config.ts", + "test/support/preload.ts!" ], "ignoreBinaries": [ "bin/*", @@ -200,10 +201,12 @@ "@nx/workspace", "graphql-tag", "pin-github-action", - "esbuild" + "esbuild", + "msw" ], "ignore": [ - "configurations/vite.config.ts" + "configurations/vite.config.ts", + "test/fixtures/**" ] }, "packages/app": { diff --git a/test/README.md b/test/README.md new file mode 100644 index 00000000000..ca1fba9f8eb --- /dev/null +++ b/test/README.md @@ -0,0 +1,103 @@ + + +# CLI command tests + +The tests in this directory execute the real CLI end to end through bash. They use isolated fixture directories, intercepted network requests, and real persistent stores. Each test file targets one command. + +## Run + +```sh +pnpm exec vitest run --project commands --reporter=verbose +``` + +## Test structure + +```ts +test('uses a saved configuration', async ({fixture}) => { + // GIVEN + await fixture.reserve('named-staging-fixture') + await fixture.seedState({appPreferences: {configFile: 'shopify.app.staging.toml'}}) + + // WHEN + const command = ['app', 'info', '--json'] + + // THEN + const result = await fixture.runShopifyCommand(command) + expect(result.exitCode, result.stderr).toBe(0) + expect(result.stderr).toBe('') + + const report = JSON.parse(result.stdout) + expect(report).toMatchObject({ + directory: fixture.projectPath, + configuration: { + client_id: 'synthetic-client-id', + name: 'Staging fixture', + application_url: 'https://fixture.example.test', + embedded: true, + access_scopes: {scopes: 'read_products'}, + }, + organization: {id: '123', businessName: 'Fixture organization'}, + }) + expect(result.requests.map(({operation}) => operation)).toEqual([ + 'UserInfo', + 'ActiveAppReleaseFromApiKey', + 'FindOrganization', + 'fetchSpecifications', + ]) + expect((await fixture.readStore('app'))[fixture.projectPath]).toMatchObject({ + configFile: 'shopify.app.staging.toml', + title: 'Remote fixture', + orgId: '123', + }) +}) +``` + +`GIVEN` prepares state. `WHEN` prepares the invocation. As requested, `THEN` starts by running the command, followed by assertions. `test.for` supplies table-driven cases and the per-test `fixture` together. + +## Fixture and command boundary + +`support/fixture.ts` creates a private sandbox and isolated home/store directories for every case. Vitest owns cleanup, including after failures. There are no automatically generated app files: every test reserves a filesystem scenario before running a command. + +`fixture.reserve(name)` copies `test/fixtures/app-information//` into the sandbox. Each scenario has a `project/` directory and can include ancestor inputs beside it. The copy is independent, writable, and reserved once per test. Cleanup checks that the source bytes, symlinks, modes, and modification times have not changed. Repeated commands reuse that copy; other tests can reserve the same source concurrently without sharing state. Relative symlinks are preserved, escaping symlinks are rejected, and source fixtures are never modified. + +The harness initializes a usable synthetic user session, empty app preferences, controlled lifecycle caches/preferences, and four baseline linked-app network mocks. Those defaults are separate from the reserved filesystem. Tests change them through the APIs below. UI scenarios contain synthetic installed-package files, so a developer's dependencies cannot supply missing exports. + +`fixture.runShopifyCommand(['app', 'info', ...flags])` starts: + +```sh +bash --noprofile --norc -c 'exec shopify "$@"' command-test app info ... +``` + +A fixture-local `shopify` shim runs Node with the test network preload and the repository's actual `packages/cli/bin/run.js`. Arguments are passed separately, never interpolated into shell source. Every invocation gets a fresh process, including cache-reuse tests that deliberately retain the fixture's disk state. + +The helper captures stdout, stderr, exit code, signal, request events, and before/after project and fixture-root snapshots. Root snapshots include home/store/framework/temp files, but exclude the harness's protocol files, launch shim, and dependency-resolution guard. Symlink targets are recorded without following them. Tests inspect persistent stores and generated files separately. The usual text report goes to stderr; JSON and web-env results go to stdout. A warning, a collected error followed by exit 2, and a thrown error before reporting are different outcomes. + +## Output expectations + +Expected outputs are inline in `app-info.test.ts`: explicit objects for JSON/configuration values, multiline strings for text reports and generated declarations, and diagnostics in the relevant test tables. Only filesystem inputs live in fixture directories; there are no external output snapshots or automatic snapshot updates. + +`support/observations.ts` normalizes exact temporary paths, including paths wrapped by Ink, and the current Node/platform values. It removes ANSI escapes, box borders, column padding, and repeated blank lines while preserving report content and paragraphs. Expectations remain in the tests, not in this helper. + +JSON assertions cover configuration, project metadata, web components, extension identity/targets/metafields, and schema omission. They deliberately do not freeze every duplicated internal extension/specification object or deploy-step manifest. Generated declarations retain complete inline comparisons, without redundant substring checks. + +Root snapshots are compared against explicit allowed changes for ordinary reporting, forced linking, parser/auth failures, and generated-file failures. In particular, an early failure does not always mean no writes: error-metadata loading can create `.shopify` files. These are final-state assertions, not filesystem write-event tracing. + +## Fixture API + +| API | Purpose | +| ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- | +| `reserve(name)` | Copy one complete filesystem scenario; no baseline app-file overlay or fixture inheritance. | +| `seedState({authentication, appPreferences, cliPreferences, caches})` | Write real persistent state at platform-specific store paths. | +| `configure({environment, cwd, clock, requestLimit, loopbackPorts})` | Configure runtime inputs and explicit harness limits. Environment keys set to `undefined` are unset in the child. | +| `mockNetwork(...)` | Register complete request/response fixtures, or override one existing GraphQL mock by operation. | +| `mockSubprocess(...)` | Script an expected external process and its outcome; never run the real browser or installer. | +| `runShopifyCommand(argv, options)` | Execute the full argument vector through bash, optionally using a PTY. | +| `path(relative)`, `projectPath`, `root` | Locate paths in the reserved project or its containing sandbox. | +| `readFile(relative)`, `readStore('app' \| 'cli-kit')` | Read UTF-8 text from the copy or the store's raw parsed JSON. | +| `writeFile(relative, contents)`, `writeFileAt(absolute, contents)`, `removeFile(relative)` | Escape hatches for runtime-dependent inputs and between-command changes. Writes are confined to the sandbox. | + +Static TOML, JSON, source, locale, and dependency bytes belong in the filesystem catalog. The two loopback tests still use `writeFile()` because their schema URLs contain a port allocated at runtime. Timestamps applied between invocations also remain runtime operations. diff --git a/test/app-info.test.ts b/test/app-info.test.ts new file mode 100644 index 00000000000..879b086958a --- /dev/null +++ b/test/app-info.test.ts @@ -0,0 +1,3715 @@ +import {loopbackServer} from './support/loopback.js' +import {changedPaths, normalizeText, storeRelativePath} from './support/observations.js' +import { + appAudience, + appManagementUrl, + businessAudience, + businessPlatformUrl, + clientId, + now, + specification, + test, +} from './support/fixture.js' +import { + authorizationUrl, + browserUrl, + errorReportingUrl, + mockErrorReport, + givenFullAuthentication, + givenInteractiveLink, + mockRefreshSession, + mockRemoteConfiguration, + tokenUrl, + mockUiSpecification, + mockLocalizedSpecification, +} from './support/scenarios.js' +import {describe, expect} from 'vitest' +import {stripVTControlCharacters} from 'node:util' +import {stat, utimes} from 'node:fs/promises' + +function text(value: string) { + return stripVTControlCharacters(value).replaceAll('│', '').replace(/\s+/g, ' ').trim() +} +function operations(result: {requests: {operation?: string}[]}) { + return result.requests.map((request) => request.operation) +} +const linkedOperations = ['UserInfo', 'ActiveAppReleaseFromApiKey', 'FindOrganization', 'fetchSpecifications'] + +describe.skipIf(process.platform === 'win32').concurrent('app info through bash', () => { + test('prints remote identity and local configuration, and records local writes', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app-dev-store') + await fixture.seedState({appPreferences: {configFile: 'shopify.app.toml', previousAppId: 'preserve-this-id'}}) + const kitBefore = await fixture.readStore('cli-kit') + // WHEN + const flags = ['--no-color'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(result.stdout).toBe('') + expect(text(result.stderr)).toContain('App name Remote fixture') + expect(text(result.stderr)).toContain(`Client ID ${clientId}`) + expect(text(result.stderr)).toContain('Organization Fixture organization (123)') + expect(text(result.stderr)).toContain('Access scopes read_products') + expect(text(result.stderr)).toContain('Dev store fixture.myshopify.com') + expect(text(result.stderr)).toContain('Update URLs No') + expect(text(result.stderr)).toContain('User fixture@example.test') + expect(text(result.stderr)).toContain('Package manager pnpm') + expect(result.stderr).not.toContain('synthetic-app-secret') + expect(operations(result)).toEqual(linkedOperations) + await expect(fixture.readFile('.shopify/.gitignore')).resolves.toBe('# Ignore the entire .shopify directory\n*') + expect(JSON.parse(await fixture.readFile('.shopify/project.json'))).toEqual({}) + for (const [path, contents] of Object.entries(result.before)) expect(result.after[path]).toBe(contents) + expect((await fixture.readStore('app'))[fixture.projectPath]).toMatchObject({ + appId: clientId, + title: 'Remote fixture', + orgId: '123', + configFile: 'shopify.app.toml', + previousAppId: 'preserve-this-id', + }) + await expect(fixture.readStore('cli-kit')).resolves.toMatchObject(kitBefore) + expect(changedPaths(result.rootBefore, result.rootAfter)).toEqual( + [ + storeRelativePath(fixture, 'app'), + storeRelativePath(fixture, 'cli-kit'), + 'project/.shopify/.gitignore', + 'project/.shopify/project.json', + ].sort(), + ) + expect((await fixture.readStore('cli-kit')).cache).toMatchObject({ + 'rate-limited-occurrences-report-analytics-event': {value: [now], timestamp: now}, + }) + }) + + describe('linking journeys', () => { + test('forced noninteractive linking rewrites configuration and repeats release/specification reads', async ({ + fixture, + }) => { + // GIVEN + await fixture.reserve('linked-app') + mockRemoteConfiguration(fixture) + // WHEN + const flags = ['--reset', '--client-id', clientId, '--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + expect(JSON.parse(result.stdout).configuration.application_url).toBe('https://remote.example.test') + await expect(fixture.readFile('shopify.app.toml')).resolves.toContain('https://remote.example.test') + expect(changedPaths(result.rootBefore, result.rootAfter)).toEqual( + [ + storeRelativePath(fixture, 'app'), + storeRelativePath(fixture, 'cli-kit'), + 'project/.shopify/.gitignore', + 'project/.shopify/project.json', + 'project/shopify.app.toml', + ].sort(), + ) + expect(operations(result).filter((operation) => operation === 'ActiveAppReleaseFromApiKey')).toHaveLength(2) + expect(operations(result).filter((operation) => operation === 'fetchSpecifications')).toHaveLength(2) + expect(result.stderr).toContain('is now linked') + }) + + test('PTY selection links an existing app', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + givenInteractiveLink(fixture, {organizations: 2}) + // WHEN + const terminal = { + replies: [ + {waitFor: 'Which organization do you want to use?', input: '\r'}, + {waitFor: 'Create this project as a new app on Shopify?', input: 'n'}, + {waitFor: 'Which existing app is this for?', input: '\r'}, + ], + } + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', '--reset'], {terminal}) + expect(result.exitCode, result.terminalOutput).toBe(0) + expect(result.answeredPrompts).toHaveLength(3) + expect(text(result.terminalOutput)).toContain('CURRENT APP CONFIGURATION') + expect(operations(result)).toContain('ListOrganizations') + expect(operations(result)).toContain('listApps') + expect(operations(result)).not.toContain('CreateApp') + }) + + test('PTY cancellation exits without creating or linking an app', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + givenInteractiveLink(fixture, {organizations: 2}) + const before = await fixture.readFile('shopify.app.toml') + fixture.mockSubprocess({command: process.platform === 'darwin' ? 'pgrep' : 'ps', exitCode: 1}) + // WHEN + const terminal = {replies: [{waitFor: 'Which organization do you want to use?', input: '\u0003'}]} + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', '--reset'], {terminal}) + expect(result.exitCode).not.toBe(0) + expect(operations(result)).not.toContain('CreateApp') + await expect(fixture.readFile('shopify.app.toml')).resolves.toBe(before) + }) + + test('PTY creation validates a name and selects the latest stable API version', async ({fixture}) => { + // GIVEN + await fixture.reserve('unlinked-client-id') + givenInteractiveLink(fixture, {create: true}) + // WHEN + const terminal = { + replies: [ + {waitFor: 'App name', input: 'Fixture creation'}, + {waitFor: 'Fixture creation', input: '\r'}, + ], + } + // THEN + const result = await fixture.runShopifyCommand(['app', 'info'], {terminal}) + expect(result.exitCode, result.terminalOutput).toBe(0) + const request = result.requests.find((entry) => entry.operation === 'CreateApp')! + expect(JSON.stringify(request.variables)).toContain('Fixture creation') + expect(JSON.stringify(request.variables)).toContain('2025-10') + expect(JSON.stringify(request.variables)).not.toContain('unstable') + await expect(fixture.readFile('shopify.app.toml')).resolves.toContain(clientId) + }) + + test('stale preferences with multiple replacements use the PTY selection', async ({fixture}) => { + // GIVEN + await fixture.reserve('named-staging') + await fixture.seedState({ + appPreferences: {configFile: 'shopify.app.deleted.toml'}, + }) + // WHEN + const terminal = {replies: [{waitFor: 'Configuration file', input: '\r'}]} + // THEN + const result = await fixture.runShopifyCommand(['app', 'info'], {terminal}) + expect(result.exitCode, result.terminalOutput).toBe(0) + expect(result.answeredPrompts).toHaveLength(1) + expect((await fixture.readStore('app'))[fixture.projectPath]).not.toMatchObject({ + configFile: 'shopify.app.deleted.toml', + }) + }) + }) + + describe('linking failures, search, and naming', () => { + test('search transforms words into a new listApps query before selection', async ({fixture}) => { + // GIVEN + await fixture.reserve('unlinked-client-id') + givenInteractiveLink(fixture) + const node = {id: fixture.remote.app.id, key: clientId, activeRelease: fixture.remote.app.activeRelease} + const selectedApp = structuredClone(fixture.remote.app) + selectedApp.id = 'gid://shopify/App/999' + selectedApp.key = 'search-result-client-id' + selectedApp.activeRelease.version.name = 'Search result fixture' + selectedApp.activeRelease.version.appModules.find((module) => module.handle === 'branding')!.config.name = + 'Search result fixture' + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + variables: {apiKey: selectedApp.key}, + responses: [{body: {data: {app: selectedApp}}}, {body: {data: {app: selectedApp}}}], + }) + fixture.mockNetwork({ + operation: 'listApps', + responses: [ + {body: {data: {appsConnection: {edges: [{node}], pageInfo: {hasNextPage: true}}}}}, + { + body: { + data: { + appsConnection: { + edges: [{node: {id: selectedApp.id, key: selectedApp.key, activeRelease: selectedApp.activeRelease}}], + pageInfo: {hasNextPage: false}, + }, + }, + }, + }, + ], + }) + // WHEN + const terminal = { + replies: [ + {waitFor: 'Create this project as a new app on Shopify?', input: 'n'}, + {waitFor: 'Which existing app is this for?', input: 'red blue'}, + {waitFor: 'Search result fixture', input: '\r'}, + ], + } + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', '--reset'], {terminal}) + expect(result.exitCode, result.terminalOutput).toBe(0) + expect(text(result.terminalOutput)).toContain('App name Search result fixture') + expect(text(result.terminalOutput)).toContain(`Client ID ${selectedApp.key}`) + await expect(fixture.readFile('shopify.app.toml')).resolves.toContain(`client_id = "${selectedApp.key}"`) + expect((await fixture.readStore('app'))[fixture.projectPath]).toMatchObject({ + appId: selectedApp.key, + title: 'Search result fixture', + }) + expect( + result.requests + .filter((request) => request.operation === 'ActiveAppReleaseFromApiKey') + .map((request) => request.variables), + ).toEqual([{apiKey: selectedApp.key}, {apiKey: selectedApp.key}]) + expect( + result.requests + .filter((request) => request.operation === 'listApps') + .map((request) => request.variables?.query), + ).toEqual(['', 'title:red title:blue']) + }) + + test.for([false, true])( + 'configuration collision allows rename or overwrite (rename=%s)', + async (rename, {fixture}) => { + // GIVEN + await fixture.reserve('config-name-collision') + mockRemoteConfiguration(fixture) + // WHEN + const terminal = { + replies: [ + {waitFor: 'Configuration file name:', input: '\r'}, + {waitFor: 'Do you want to choose a different configuration name?', input: rename ? 'y' : 'n'}, + ...(rename + ? [ + { + waitFor: 'Configuration file name:', + input: [...Array.from({length: 'Remote fixture'.length}, () => '\u007f'), 'staging-new', '\r'], + }, + ] + : []), + ], + } + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', '--reset', '--client-id', clientId], {terminal}) + expect(result.exitCode, result.terminalOutput).toBe(0) + const filename = rename ? 'shopify.app.staging-new.toml' : 'shopify.app.remote-fixture.toml' + await expect(fixture.readFile(filename)).resolves.toContain(clientId) + await expect(fixture.readFile('shopify.app.toml')).resolves.toContain('other-client') + expect((await fixture.readStore('app'))[fixture.projectPath]).toMatchObject({configFile: filename}) + }, + ) + + test.for([ + {name: '', error: "App name can't be empty"}, + {name: 'x'.repeat(31), error: 'Enter a shorter name'}, + {name: 'shopify', error: "Name can't contain"}, + ])('invalid app name $name cannot reach creation', async ({name, error}, {fixture}) => { + // GIVEN + await fixture.reserve('unlinked-client-id') + givenInteractiveLink(fixture, {create: true}) + fixture.mockSubprocess({command: process.platform === 'darwin' ? 'pgrep' : 'ps', exitCode: 1}) + // WHEN + const terminal = { + replies: [ + {waitFor: 'App name', input: name ? [name, '\r'] : '\r'}, + {waitFor: error, input: '\u0003'}, + ], + } + // THEN + const result = await fixture.runShopifyCommand(['app', 'info'], {terminal}) + expect(result.exitCode).not.toBe(0) + expect(text(result.terminalOutput)).toContain(error) + expect(operations(result)).not.toContain('CreateApp') + }) + + test.for([false, true])( + 'creation failure preserves local TOML (failure after creation=%s)', + async (afterCreation, {fixture}) => { + // GIVEN + await fixture.reserve('unlinked-client-id') + const original = 'client_id = ""\n' + givenInteractiveLink(fixture, {create: true}) + if (afterCreation) + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [{status: 403, body: {errors: [{message: 'Later release failure'}]}}], + }) + else + fixture.mockNetwork({ + operation: 'CreateApp', + responses: [ + { + body: { + data: { + appCreate: {app: null, userErrors: [{category: 'invalid', message: 'Creation refused', on: {}}]}, + }, + }, + }, + ], + }) + // WHEN + const terminal = { + replies: [ + {waitFor: 'App name', input: 'Fixture creation'}, + {waitFor: 'Fixture creation', input: '\r'}, + ], + } + // THEN + const result = await fixture.runShopifyCommand(['app', 'info'], {terminal}) + expect(result.exitCode).not.toBe(0) + expect(text(result.terminalOutput)).toContain(afterCreation ? 'Later release failure' : 'Creation refused') + expect(operations(result).filter((operation) => operation === 'CreateApp')).toHaveLength(1) + await expect(fixture.readFile('shopify.app.toml')).resolves.toBe(original) + expect(operations(result).filter((operation) => /delete/i.test(operation ?? ''))).toEqual([]) + }, + ) + }) + + describe('device authorization journeys', () => { + test.for([false, true])('full device authentication completes (pending=%s)', async (pending, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + await givenFullAuthentication(fixture, pending ? [{status: 400, body: {error: 'authorization_pending'}}] : []) + // WHEN + const terminal = {replies: []} + // THEN + const result = await fixture.runShopifyCommand(['app', 'info'], {terminal}) + expect(result.exitCode, result.terminalOutput).toBe(0) + expect(text(result.terminalOutput)).toContain('User fixture@example.test') + expect( + result.requests.filter((request) => + new URLSearchParams(request.body).get('grant_type')?.endsWith('device_code'), + ), + ).toHaveLength(pending ? 2 : 1) + const stored = JSON.parse((await fixture.readStore('cli-kit')).sessionStore as string) + expect(stored['accounts.shopify.com']['synthetic-user'].identity.alias).toBe('login@example.test') + }) + + test('desktop login invokes only the scripted browser opener', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + await givenFullAuthentication(fixture) + fixture.configure({ + environment: { + CODESPACES: undefined, + }, + }) + fixture.mockSubprocess({command: process.platform === 'darwin' ? 'open' : 'xdg-open', args: [browserUrl]}) + // WHEN + const terminal = {replies: []} + // THEN + const result = await fixture.runShopifyCommand(['app', 'info'], {terminal}) + expect(result.exitCode, result.terminalOutput).toBe(0) + expect( + result.events.filter((event) => event.type === 'spawn').some((event) => event.args?.includes(browserUrl)), + ).toBe(true) + expect(text(result.terminalOutput)).toContain('Opened link to start the auth process') + }) + + test.for(['access_denied', 'expired_token'])( + 'device polling %s aborts before app lookup', + async (error, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + await givenFullAuthentication(fixture, [{status: 400, body: {error}}]) + // WHEN + const terminal = {replies: []} + // THEN + const result = await fixture.runShopifyCommand(['app', 'info'], {terminal}) + expect(result.exitCode).not.toBe(0) + expect(text(result.terminalOutput)).toContain('Device authorization failed') + expect(operations(result)).not.toContain('ActiveAppReleaseFromApiKey') + }, + ) + }) + + describe('polling and authentication edge cases', () => { + test('slow_down increases the next polling interval', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + await givenFullAuthentication(fixture, [{status: 400, body: {error: 'slow_down'}}]) + // WHEN + const terminal = {replies: []} + // THEN + const result = await fixture.runShopifyCommand(['app', 'info'], {terminal}) + expect(result.exitCode, result.terminalOutput).toBe(0) + const polls = result.requests.filter((request) => + new URLSearchParams(request.body).get('grant_type')?.endsWith('device_code'), + ) + expect(polls).toHaveLength(2) + expect(polls[1]!.elapsedMs! - polls[0]!.elapsedMs!).toBeGreaterThanOrEqual(4900) + }) + + test('endless pending authorization remains bounded by the harness deadline', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.configure({ + requestLimit: 100, + }) + await givenFullAuthentication(fixture) + const start = fixture.networkMocks.find((request) => request.url === authorizationUrl)! + start.responses = [ + { + body: { + device_code: 'fixture-device', + user_code: 'FIXTURE', + verification_uri_complete: browserUrl, + verification_uri: browserUrl, + interval: 0.1, + expires_in: 0, + }, + }, + ] + const poll = fixture.networkMocks.find((request) => request.form?.grant_type?.endsWith('device_code'))! + poll.responses = [{status: 400, body: {error: 'authorization_pending'}}] + poll.repeatLastResponse = true + // WHEN + const terminal = {replies: []} + // THEN + const result = await fixture.runShopifyCommand(['app', 'info'], {terminal, timeoutMs: 5000, allowTimeout: true}) + expect(result.timedOut).toBe(true) + expect(result.requests.filter((request) => request.url === tokenUrl).length).toBeGreaterThan(1) + expect(operations(result)).not.toContain('ActiveAppReleaseFromApiKey') + }) + + test('UserEmail failure falls back to the user ID without preventing app info', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + await givenFullAuthentication(fixture) + fixture.mockNetwork({ + operation: 'UserEmail', + responses: [{status: 403, body: {errors: [{message: 'Email unavailable'}]}}], + }) + // WHEN + const terminal = {replies: []} + // THEN + const result = await fixture.runShopifyCommand(['app', 'info'], {terminal}) + expect(result.exitCode, result.terminalOutput).toBe(0) + const sessions = JSON.parse((await fixture.readStore('cli-kit')).sessionStore as string) + expect(sessions['accounts.shopify.com']['synthetic-user'].identity.alias).toBe('synthetic-user') + }) + + test('valid device authorization response in CI aborts without polling', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + await givenFullAuthentication(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.requests.map((request) => request.url)).toEqual([authorizationUrl]) + expect(text(result.stderr)).toContain('Authorization is required') + }) + + test.for(['app-management', 'business-platform'])( + 'automation exchange rejection identifies the failed %s API', + async (api, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.configure({ + environment: { + SHOPIFY_APP_AUTOMATION_TOKEN: 'synthetic-automation', + }, + }) + fixture.mockTokenExchange(appAudience, 'synthetic-app-token') + fixture.mockTokenExchange(businessAudience, 'synthetic-business-token') + fixture.networkMocks.find( + (request) => request.form?.audience === (api === 'app-management' ? appAudience : businessAudience), + )!.responses = [{status: 400, body: {error: 'invalid_target'}}] + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(text(result.stderr)).toContain("The custom token provided can't be used") + expect(result.requests).toHaveLength(api === 'app-management' ? 1 : 2) + }, + ) + + test('empty new automation variable suppresses the legacy-token fallback', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.configure({ + environment: { + SHOPIFY_APP_AUTOMATION_TOKEN: '', + }, + }) + fixture.configure({ + environment: { + SHOPIFY_CLI_PARTNERS_TOKEN: 'synthetic-legacy-token', + }, + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(operations(result)).toEqual(linkedOperations) + }) + }) + + describe('OAuth response and exchange failures', () => { + test.for([ + { + name: 'missing ID token', + response: {body: {access_token: 'new-token', refresh_token: 'new-refresh', expires_in: 3600, scope: 'openid'}}, + message: 'No id_token', + reportError: true, + }, + { + name: 'invalid ID token', + response: { + body: { + access_token: 'new-token', + refresh_token: 'new-refresh', + expires_in: 3600, + scope: 'openid', + id_token: 'not-a-jwt', + }, + }, + message: 'Invalid JWT', + reportError: true, + }, + { + name: 'non-JSON success', + response: {text: 'not JSON'}, + message: 'Received invalid response from authentication service', + reportError: false, + }, + ])( + 'rejects device-token $name without persisting credentials or loading the app', + async ({response, message, reportError}, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + await givenFullAuthentication(fixture) + fixture.networkMocks.find((request) => request.form?.grant_type?.endsWith('device_code'))!.responses = [ + response, + ] + if (reportError) mockErrorReport(fixture) + // WHEN + const terminal = {replies: []} + // THEN + const result = await fixture.runShopifyCommand(['app', 'info'], {terminal}) + expect(result.exitCode).toBe(1) + expect(text(result.terminalOutput)).toContain(message) + expect(result.terminalOutput).not.toContain('CURRENT APP CONFIGURATION') + expect(result.requests.filter((request) => new URLSearchParams(request.body).has('audience'))).toEqual([]) + expect(operations(result)).not.toContain('ActiveAppReleaseFromApiKey') + expect(JSON.stringify(await fixture.readStore('cli-kit'))).not.toContain('new-token') + // Error metadata performs a local load even though authentication failed. + expect(changedPaths(result.after, result.before)).toEqual(['.shopify/.gitignore', '.shopify/project.json']) + expect(changedPaths(result.rootBefore, result.rootAfter)).toEqual([ + 'project/.shopify/.gitignore', + 'project/.shopify/project.json', + ]) + }, + ) + + test.for([ + appAudience, + businessAudience, + 'ee139b3d-5861-4d45-b387-1bc3ada7811c', + '271e16d403dfa18082ffb3d197bd2b5f4479c3fc32736d69296829cbb28d41a6', + ])( + 'an independent user-token exchange failure for %s prevents the report and session persistence', + async (audience, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + await givenFullAuthentication(fixture) + fixture.networkMocks.find((request) => request.form?.audience === audience)!.responses = [ + {status: 400, body: {error: 'invalid_target'}}, + ] + // WHEN + const terminal = {replies: []} + // THEN + const result = await fixture.runShopifyCommand(['app', 'info'], {terminal}) + expect(result.exitCode).toBe(1) + expect(text(result.terminalOutput)).toContain('You are not authorized to use the CLI') + expect(result.terminalOutput).not.toContain('CURRENT APP CONFIGURATION') + expect(result.requests.some((request) => new URLSearchParams(request.body).get('audience') === audience)).toBe( + true, + ) + expect(operations(result)).not.toContain('ActiveAppReleaseFromApiKey') + expect(JSON.stringify(await fixture.readStore('cli-kit'))).not.toContain('synthetic-new-identity') + }, + ) + }) + + describe('unauthorized recovery and cache boundaries', () => { + test.for(['ActiveAppReleaseFromApiKey', 'FindOrganization'])( + '401 refreshes and replays %s with the right audience', + async (operation, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + const request = fixture.networkMocks.find((entry) => entry.operation === operation)! + const success = request.responses[0]! + const refreshed = {app: 'refreshed-app-token', business: 'refreshed-business-token'} + const oldAuthorization = request.headers!.Authorization + const newAuthorization = `Bearer ${operation === 'FindOrganization' ? refreshed.business : refreshed.app}` + fixture.mockNetwork({operation, responses: [{status: 401, body: {errors: [{message: 'Unauthorized'}]}}]}) + // Subsequent operations and the replay must use the newly exchanged tokens. + for (const subsequent of fixture.networkMocks.slice(fixture.networkMocks.indexOf(request) + 1)) { + subsequent.headers = { + Authorization: `Bearer ${subsequent.url === businessPlatformUrl ? refreshed.business : refreshed.app}`, + } + } + fixture.mockNetwork({...request, headers: {Authorization: newAuthorization}, responses: [success]}) + mockRefreshSession(fixture, refreshed) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + const attempts = result.requests.filter((entry) => entry.operation === operation) + expect(attempts.map((entry) => entry.headers?.authorization)).toEqual([oldAuthorization, newAuthorization]) + expect( + result.requests.filter((entry) => new URLSearchParams(entry.body).get('grant_type') === 'refresh_token'), + ).toHaveLength(1) + const exchanges = result.requests.filter((entry) => new URLSearchParams(entry.body).has('audience')) + expect(exchanges).toHaveLength(4) + for (const exchange of exchanges) { + expect(new URLSearchParams(exchange.body).get('subject_token')).toBe('synthetic-refreshed-identity') + } + const stored = JSON.parse((await fixture.readStore('cli-kit')).sessionStore as string) + expect(stored['accounts.shopify.com']['synthetic-user']).toMatchObject({ + identity: {accessToken: 'synthetic-refreshed-identity', refreshToken: 'synthetic-refreshed-refresh'}, + applications: { + [appAudience]: {accessToken: refreshed.app}, + [businessAudience]: {accessToken: refreshed.business}, + }, + }) + expect(JSON.parse(result.stdout).organization).toEqual({id: '123', businessName: 'Fixture organization'}) + expect(result.stderr).toBe('') + }, + ) + + test('persistent 401 stops after the refreshed replay', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [ + ...Array.from({length: 2}, () => ({status: 401, body: {errors: [{message: 'Still unauthorized'}]}})), + ], + }) + mockRefreshSession(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(operations(result).filter((operation) => operation === 'ActiveAppReleaseFromApiKey')).toHaveLength(2) + expect(operations(result)).not.toContain('FindOrganization') + }) + + test('no-prompt refresh failure aborts instead of opening a browser', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [{status: 401, body: {errors: [{message: 'Unauthorized'}]}}], + }) + fixture.mockNetwork({ + method: 'POST', + url: tokenUrl, + form: {grant_type: 'refresh_token'}, + responses: [{status: 400, body: {error: 'invalid_grant'}}], + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.requests.some((request) => request.url === authorizationUrl)).toBe(false) + expect(result.events.filter((event) => event.type === 'spawn')).toEqual([]) + await expect(fixture.readStore('cli-kit')).resolves.not.toHaveProperty('sessionStore') + }) + + test('initial refresh invalid_grant falls back to full authentication', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + const user = structuredClone(fixture.user) + user.identity.expiresAt = '2000-01-01T00:00:00Z' + await givenFullAuthentication(fixture) + await fixture.seedState({ + authentication: { + sessions: { + 'accounts.shopify.com': { + 'synthetic-user': user, + }, + }, + }, + }) + fixture.mockNetwork({ + method: 'POST', + url: tokenUrl, + form: {grant_type: 'refresh_token'}, + responses: [{status: 400, body: {error: 'invalid_grant'}}], + }) + // WHEN + const terminal = {replies: []} + // THEN + const result = await fixture.runShopifyCommand(['app', 'info'], {terminal}) + expect(result.exitCode, result.terminalOutput).toBe(0) + expect(result.requests.some((request) => request.url === authorizationUrl)).toBe(true) + expect(operations(result)).toContain('ActiveAppReleaseFromApiKey') + }) + + test.for([-1, 0, 1])('query cache expiry boundary offset %s milliseconds', async (offset, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + await fixture.runShopifyCommand(['app', 'info', '--json']) + const store = await fixture.readStore('cli-kit') + const cache = store.cache as Record< + string, + { + timestamp: number + value: unknown + } + > + for (const [key, value] of Object.entries(cache)) + if (key.startsWith('q-')) value.timestamp = now - 6 * 60 * 60 * 1000 - offset + await fixture.seedState({ + caches: cache, + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(operations(result)).toEqual( + offset < 0 ? ['ActiveAppReleaseFromApiKey', 'fetchSpecifications'] : linkedOperations, + ) + }) + + test('corrupt query cache fails without a silent network refresh', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + await fixture.runShopifyCommand(['app', 'info', '--json']) + const cache = (await fixture.readStore('cli-kit')).cache as Record< + string, + { + timestamp: number + value: unknown + } + > + const key = Object.keys(cache).find((key) => key.startsWith('q-') && key.endsWith('-synthetic-user'))! + cache[key]!.value = '{invalid' + await fixture.seedState({ + caches: cache, + }) + mockErrorReport(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(operations(result)).not.toContain('UserInfo') + expect(result.stdout).toBe('') + }) + + test('expired Business Platform credentials reach the API without proactive refresh', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + const user = structuredClone(fixture.user) + user.applications[businessAudience].expiresAt = '2000-01-01T00:00:00Z' + await fixture.seedState({ + authentication: { + sessions: { + 'accounts.shopify.com': { + 'synthetic-user': user, + }, + }, + }, + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(operations(result)).toEqual(linkedOperations) + expect(result.requests[0]!.headers?.authorization).toBe('Bearer synthetic-business-token') + }) + }) + + describe('multiple accounts and credential selection', () => { + test.for([undefined, 'secondary'])( + 'alias override chooses the first matching account and preserves saved selection %s', + async (selected, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + const secondary = structuredClone(fixture.user) + secondary.identity.userId = 'secondary' + secondary.applications[businessAudience].accessToken = 'wrong-account-token' + await fixture.seedState({ + authentication: { + currentSessionId: selected, + sessions: {'accounts.shopify.com': {'synthetic-user': fixture.user, secondary}}, + }, + }) + // WHEN + const flags = ['--auth-alias', 'fixture', '--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(result.requests[0]!.headers?.authorization).toBe('Bearer synthetic-business-token') + expect((await fixture.readStore('cli-kit')).currentSessionId).toBe(selected) + }, + ) + + test('absent saved selection uses the first account without persisting a default', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + const secondary = structuredClone(fixture.user) + secondary.identity.userId = 'secondary' + secondary.applications[businessAudience].accessToken = 'wrong-account-token' + await fixture.seedState({ + authentication: { + currentSessionId: undefined, + sessions: {'accounts.shopify.com': {'synthetic-user': fixture.user, secondary}}, + }, + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(result.requests[0]!.headers?.authorization).toBe('Bearer synthetic-business-token') + await expect(fixture.readStore('cli-kit')).resolves.not.toHaveProperty('currentSessionId') + }) + + test('stale saved selection does not fall back to another stored account', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + await fixture.seedState({ + authentication: { + currentSessionId: 'missing-user', + }, + }) + fixture.mockNetwork({ + method: 'POST', + url: authorizationUrl, + responses: [{body: {device_code: 'fixture', verification_uri_complete: browserUrl}}], + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.requests.map((request) => request.url)).toEqual([authorizationUrl]) + }) + + test('organization cache is reused across accounts while UserInfo is not', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + await fixture.runShopifyCommand(['app', 'info', '--json']) + const secondary = structuredClone(fixture.user) + secondary.identity.userId = 'secondary' + secondary.applications[businessAudience].accessToken = 'secondary-business-token' + await fixture.seedState({ + authentication: { + currentSessionId: 'secondary', + sessions: {'accounts.shopify.com': {'synthetic-user': fixture.user, secondary}}, + }, + }) + for (const request of fixture.networkMocks) + if (request.url === businessPlatformUrl) request.headers = {Authorization: 'Bearer secondary-business-token'} + fixture.mockNetwork({ + operation: 'UserInfo', + responses: [ + { + body: { + data: { + currentUserAccount: {uuid: 'secondary', email: 'secondary@example.test', organizations: {nodes: []}}, + }, + }, + }, + ], + }) + // WHEN + const flags: string[] = [] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(text(result.stderr)).toContain('User secondary@example.test') + expect(operations(result)).toEqual(['UserInfo', 'ActiveAppReleaseFromApiKey', 'fetchSpecifications']) + }) + + test.for([appAudience, businessAudience])( + 'missing audience %s has its documented refresh boundary', + async (audience, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + const applications: Record = {...fixture.user.applications} + delete applications[audience] + await fixture.seedState({ + authentication: { + sessions: { + 'accounts.shopify.com': {'synthetic-user': {identity: fixture.user.identity, applications}}, + }, + }, + }) + if (audience === appAudience) mockRefreshSession(fixture) + else mockErrorReport(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode === 0).toBe(audience === appAudience) + expect(result.requests.some((request) => request.url === tokenUrl)).toBe(audience === appAudience) + if (audience === businessAudience) expect(result.stdout).toBe('') + }, + ) + + test.for([0, 2])('legacy automation credentials with %s organizations', async (count, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.configure({ + environment: { + SHOPIFY_CLI_PARTNERS_TOKEN: 'synthetic-legacy-token', + }, + }) + fixture.mockTokenExchange(appAudience, 'synthetic-app-token') + fixture.mockTokenExchange(businessAudience, 'synthetic-business-token') + fixture.mockNetwork({ + operation: 'UserInfo', + responses: [ + { + body: { + data: { + currentUserAccount: { + uuid: 'service', + email: 'service@example.test', + organizations: {nodes: Array.from({length: count}, () => ({name: 'Fixture organization'}))}, + }, + }, + }, + }, + ], + }) + if (count === 2) mockErrorReport(fixture) + // WHEN + const flags: string[] = [] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode === 0).toBe(count === 0) + expect(text(result.stderr)).toContain( + count === 0 ? 'Service account Unknown organization' : 'Multiple organizations found for the CLI token', + ) + }) + }) + + describe('loopback transport boundaries', () => { + test('native schema fetch follows a real loopback redirect', async ({fixture, onTestFinished}) => { + // GIVEN + await fixture.reserve('ui-tools-missing-file') + const server = await loopbackServer((request, response) => { + if (request.url === '/redirect') { + response.writeHead(302, {Location: '/schema.json'}) + response.end() + return + } + response.writeHead(200, {'Content-Type': 'application/json'}) + response.end(JSON.stringify({type: 'object', properties: {loopbackField: {type: 'string'}}})) + }) + onTestFinished(server.close) + fixture.configure({ + loopbackPorts: [server.port], + }) + mockUiSpecification(fixture) + await fixture.writeFile( + 'extensions/ui/tools.json', + JSON.stringify([{name: 'lookup', description: '', inputSchema: {$ref: `${server.url}/redirect`}}]), + ) + fixture.mockNetwork({method: 'GET', url: `${server.url}/redirect`, passthrough: true, responses: []}) + fixture.mockNetwork({method: 'GET', url: `${server.url}/schema.json`, passthrough: true, responses: []}) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + expect(server.paths).toEqual(['/redirect', '/schema.json']) + await expect(fixture.readFile('extensions/ui/shopify.d.ts')).resolves.toContain('loopbackField') + }) + + test('socket failure prints the report but leaves the schema resolver timer alive', async ({ + fixture, + onTestFinished, + }) => { + // GIVEN + await fixture.reserve('ui-tools-missing-file') + const server = await loopbackServer((request) => request.socket.destroy()) + onTestFinished(server.close) + fixture.configure({ + loopbackPorts: [server.port], + }) + mockUiSpecification(fixture) + await fixture.writeFile( + 'extensions/ui/tools.json', + JSON.stringify([{name: 'lookup', description: '', inputSchema: {$ref: `${server.url}/schema.json`}}]), + ) + fixture.mockNetwork({method: 'GET', url: `${server.url}/schema.json`, passthrough: true, responses: []}) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags], { + timeoutMs: 15000, + terminateAfterStdout: (stdout) => { + try { + JSON.parse(stdout) + return true + } catch (error) { + if (error instanceof SyntaxError) return false + throw error + } + }, + }) + // The dependency clears its 60-second timer only after a successful fetch. + // Characterize the hang after the complete report arrives, without waiting a minute. + expect(result.terminatedAfterStdout).toBe(true) + expect(result.timedOut).toBe(false) + expect(JSON.parse(result.stdout).configuration.client_id).toBe(clientId) + expect(server.paths).toContain('/schema.json') + expect(result.stderr).toContain('Failed to create tools type definition') + }) + }) + + describe('response envelopes and request deadlines', () => { + test.for([false, true])( + 'transient socket failure obeys skip-network-retry=%s', + async (skip, {fixture, onTestFinished}) => { + // GIVEN + await fixture.reserve('linked-app') + let attempts = 0 + const server = await loopbackServer((request, response) => { + attempts += 1 + if (attempts === 1) { + request.socket.destroy() + return + } + response.writeHead(200, {'Content-Type': 'application/json'}) + response.end(JSON.stringify({data: {app: fixture.remote.app}})) + }) + onTestFinished(server.close) + fixture.configure({ + loopbackPorts: [server.port], + environment: {SHOPIFY_CLI_SKIP_NETWORK_LEVEL_RETRY: skip ? '1' : undefined}, + }) + // Redirect the intercepted API call to an owned socket, never a real service. + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [{status: 307, headers: {Location: `${server.url}/graphql`}}], + repeatLastResponse: true, + }) + fixture.mockNetwork({method: 'POST', url: `${server.url}/graphql`, passthrough: true, responses: []}) + // WHEN + const argv = ['app', 'info', '--json'] + // THEN + const result = await fixture.runShopifyCommand(argv) + expect(result.exitCode, result.stderr).toBe(skip ? 1 : 0) + expect( + result.requests.filter( + (request) => request.url === appManagementUrl && request.operation === 'ActiveAppReleaseFromApiKey', + ), + ).toHaveLength(skip ? 1 : 2) + expect(server.paths).toEqual(skip ? ['/graphql'] : ['/graphql', '/graphql']) + if (skip) { + expect(result.stdout).toBe('') + // A peer reset can surface either message depending on socket timing/OS. + expect(text(result.stderr)).toMatch(/socket hang up|ECONNRESET/) + expect(text(result.stderr)).toContain(`${server.url}/graphql`) + expect(operations(result)).not.toContain('FindOrganization') + } else { + expect(JSON.parse(result.stdout).configuration.client_id).toBe(clientId) + expect(result.stderr).toBe('') + } + }, + ) + + test.for(['', 'proxy failure', '{broken'])('rejects response body %j', async (body, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [{text: body}], + }) + mockErrorReport(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(operations(result)).not.toContain('FindOrganization') + }) + + test.for([{}, {data: null}, {data: {app: {activeRelease: null}}}])( + 'rejects malformed response envelope %j', + async (body, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [{body}], + }) + mockErrorReport(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + }, + ) + + test.for(['THROTTLED', '429', 429])( + 'GraphQL throttle code %j uses only string retry codes', + async (code, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [ + {headers: {'Retry-After': '0'}, body: {errors: [{message: 'Throttled', extensions: {code}}]}}, + {body: {data: {app: fixture.remote.app}}}, + ], + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(typeof code === 'number' ? 1 : 0) + expect(operations(result).filter((operation) => operation === 'ActiveAppReleaseFromApiKey')).toHaveLength( + typeof code === 'number' ? 1 : 2, + ) + }, + ) + + test('a response past the request deadline cannot become a successful report', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.configure({ + environment: { + SHOPIFY_CLI_MAX_REQUEST_TIME_FOR_NETWORK_CALLS: '500', + }, + }) + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [{delayMs: 1500, body: {data: {app: fixture.remote.app}}}], + }) + mockErrorReport(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.timedOut).toBe(false) + expect(result.stdout).toBe('') + expect(operations(result)).not.toContain('FindOrganization') + }) + + test('present response extensions without deprecations expose the callback failure', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [{body: {data: {app: fixture.remote.app}, extensions: {}}}], + }) + mockErrorReport(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(result.stderr).toContain('deprecations') + }) + }) + + describe('discovery and filesystem boundaries', () => { + test('inactive config web directories restrict default discovery', async ({fixture}) => { + // GIVEN + await fixture.reserve('inactive-web-directories') + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout).webs).toHaveLength(1) + expect(JSON.parse(result.stdout).webs[0].directory).toContain('other') + }) + + test('hidden web files are discovered but node_modules web files are excluded', async ({fixture}) => { + // GIVEN + await fixture.reserve('hidden-web-discovery') + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(2) + expect(JSON.parse(result.stdout).webs).toHaveLength(2) + expect(result.stdout).not.toContain('node_modules/dependency') + }) + + test.for([ + {fixtureName: 'hidden-null', contents: 'null'}, + {fixtureName: 'hidden-boolean', contents: 'true'}, + {fixtureName: 'hidden-number', contents: '42'}, + {fixtureName: 'hidden-array', contents: '[]'}, + ])('hidden project JSON root $fixtureName has its own outcome', async ({fixtureName, contents}, {fixture}) => { + // GIVEN + await fixture.reserve(fixtureName) + if (contents === 'null') mockErrorReport(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode === 0).toBe(contents !== 'null') + await expect(fixture.readFile('.shopify/project.json')).resolves.toBe(contents) + }) + + test('a file where .shopify must be a directory aborts required writes', async ({fixture}) => { + // GIVEN + await fixture.reserve('hidden-directory-collision') + mockErrorReport(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + await expect(fixture.readFile('.shopify')).resolves.toBe('not a directory') + }) + + test('dev-store TOML including an empty string takes precedence over hidden state', async ({fixture}) => { + // GIVEN + await fixture.reserve('empty-dev-store') + // WHEN + const flags: string[] = [] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(result.stderr).not.toContain('hidden.myshopify.com') + expect(text(result.stderr)).not.toContain('Dev store Not yet configured') + }) + + test('nearest project marker wins over a higher-priority ancestor marker', async ({fixture}) => { + // GIVEN + await fixture.reserve('package-marker-ancestor') + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout).packageManager).toBe('npm') + }) + + test.for(['yarn', 'pnpm', 'bun', 'npm'])( + 'package manager falls back to %s user-agent without markers', + async (manager, {fixture}) => { + // GIVEN + await fixture.reserve('no-package-manager-marker') + fixture.configure({ + environment: { + npm_config_user_agent: `${manager}/1.0.0`, + }, + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout).packageManager).toBe(manager) + }, + ) + + test('workspace marker presence is independent of its bytes', async ({fixture}) => { + // GIVEN + await fixture.reserve('workspace-marker') + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout).usesWorkspaces).toBe(true) + }) + + test('nested invocation reads root preference but writes metadata under the invoked path', async ({fixture}) => { + // GIVEN + await fixture.reserve('named-staging') + await fixture.seedState({ + appPreferences: {configFile: 'shopify.app.staging.toml'}, + }) + // WHEN + const cwd = fixture.path('web') + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', '--json'], {cwd}) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout).configuration.name).toBe('Staging') + expect((await fixture.readStore('app'))[cwd]).toMatchObject({title: 'Remote fixture'}) + }) + }) + + describe('web normalization and historical linked configurations', () => { + test.for([ + { + fixtureName: 'web-roles-win', + roles: ['backend'], + extra: { + auth_callback_path: ['/auth/callback', '/second'], + webhooks_path: '/hooks', + port: 65536, + hmr_server: {http_paths: ['hmr']}, + commands: {dev: '', build: 'never-build', predev: 'never-predev'}, + }, + }, + {fixtureName: 'web-invalid-roles-fallback', roles: ['background'], extra: {auth_callback_path: '/callback'}}, + {fixtureName: 'web-default-frontend', roles: ['frontend'], extra: {}}, + {fixtureName: 'web-empty-roles', roles: [], extra: {}}, + {fixtureName: 'web-port-zero', roles: ['frontend'], extra: {port: 0}}, + {fixtureName: 'web-port-fraction', roles: ['frontend'], extra: {port: 1.5}}, + ])('normalizes $fixtureName without rewriting or executing it', async ({fixtureName, roles, extra}, {fixture}) => { + // GIVEN + await fixture.reserve(fixtureName) + // WHEN + const argv = ['app', 'info', '--json'] + // THEN + const result = await fixture.runShopifyCommand(argv) + expect(result.exitCode, result.stderr).toBe(0) + const report = JSON.parse(result.stdout) + expect(report.webs).toHaveLength(1) + expect(report.webs[0].configuration).toMatchObject({roles, ...extra}) + expect(report.webs[0].configuration).not.toHaveProperty('type') + expect(report.webs[0].configuration).not.toHaveProperty('unknown') + expect(result.after['web/shopify.web.toml']).toBe(result.before['web/shopify.web.toml']) + expect(result.events.filter((event) => event.type === 'spawn')).toEqual([]) + expect(result.stderr).toBe('') + }) + + test.for([ + {fixtureName: 'web-both-branches-invalid', field: 'roles'}, + {fixtureName: 'web-hmr-missing-paths', field: 'http_paths'}, + {fixtureName: 'web-port-negative', field: 'port'}, + {fixtureName: 'web-port-too-large', field: 'port'}, + {fixtureName: 'web-port-string', field: 'port'}, + ])('reports the rejected web field in $fixtureName', async ({fixtureName, field}, {fixture}) => { + // GIVEN + await fixture.reserve(fixtureName) + // WHEN + const argv = ['app', 'info', '--json'] + // THEN + const result = await fixture.runShopifyCommand(argv) + expect(result.exitCode, result.stderr).toBe(2) + const report = JSON.parse(result.stdout) + expect(report.configuration.client_id).toBe(clientId) + expect(report.webs).toEqual([]) + expect(JSON.stringify(report.errors)).toContain(field) + expect(result.after['web/shopify.web.toml']).toBe(result.before['web/shopify.web.toml']) + expect(result.stderr).toBe('') + }) + + test('reports the detected framework from web dependencies rather than the web name', async ({fixture}) => { + // GIVEN + await fixture.reserve('web-remix-framework') + // WHEN + const argv = ['app', 'info', '--json'] + // THEN + const result = await fixture.runShopifyCommand(argv) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout).webs[0]).toMatchObject({ + framework: 'remix', + configuration: {name: 'Fixture web'}, + }) + expect(result.stderr).toBe('') + expect(result.events.filter((event) => event.type === 'spawn')).toEqual([]) + }) + + test.for([ + { + fixtureName: 'linked-legacy-privacy', + expectedFields: { + webhooks: { + api_version: '2023-07', + privacy_compliance: { + customer_deletion_url: '/privacy/delete', + customer_data_request_url: '/privacy/data', + shop_deletion_url: '/privacy/shop', + }, + }, + }, + }, + { + fixtureName: 'linked-uri-shorthand', + // The current loader drops the unsupported top-level URI/topics from the report. + expectedFields: {webhooks: {api_version: '2023-10'}}, + }, + { + fixtureName: 'linked-old-subscription', + // sub_topic and metafield_namespaces are not retained in the normalized report. + expectedFields: { + webhooks: {api_version: '2024-01', subscriptions: [{topics: ['orders/create'], uri: '/hooks'}]}, + }, + }, + { + fixtureName: 'linked-required-scopes', + expectedFields: { + build: {include_config_on_deploy: true}, + access_scopes: {required_scopes: ['read_products', 'write_products'], optional_scopes: ['read_orders']}, + webhooks: { + api_version: '2025-10', + subscriptions: [ + {compliance_topics: ['customers/data_request', 'customers/redact', 'shop/redact'], uri: '/privacy'}, + ], + }, + }, + }, + ])( + 'normalizes linked compatibility input $fixtureName without rewriting it', + async ({fixtureName, expectedFields}, {fixture}) => { + // GIVEN + await fixture.reserve(fixtureName) + fixture.remote.specifications.push( + ...['webhooks', 'webhook_subscription', 'privacy_compliance_webhooks'].map((id) => specification(id)), + ) + // WHEN + const argv = ['app', 'info', '--json'] + // THEN + const result = await fixture.runShopifyCommand(argv) + expect(result.exitCode, result.stderr).toBe(0) + expect(result.stderr).toBe('') + const report = JSON.parse(result.stdout) + expect(report.configuration).toEqual({ + client_id: clientId, + name: 'Local fixture', + application_url: 'https://fixture.example.test', + embedded: true, + access_scopes: {scopes: 'read_products'}, + auth: {redirect_urls: ['https://fixture.example.test/auth/callback']}, + ...expectedFields, + }) + expect(report.errors).toEqual({errors: []}) + expect(result.after['shopify.app.toml']).toBe(result.before['shopify.app.toml']) + }, + ) + + test.for([ + {fixtureName: 'linked-endpoint-webhooks', diagnostic: '[webhooks.subscriptions.0.uri]: Required'}, + { + fixtureName: 'linked-mixed-privacy', + diagnostic: + "[webhooks]: The privacy_compliance section can't be used if there are subscriptions including compliance_topics", + }, + ])( + 'rejects incompatible linked input $fixtureName without rewriting it', + async ({fixtureName, diagnostic}, {fixture}) => { + // GIVEN + await fixture.reserve(fixtureName) + fixture.remote.specifications.push( + ...['webhooks', 'webhook_subscription', 'privacy_compliance_webhooks'].map((id) => specification(id)), + ) + // WHEN + const argv = ['app', 'info', '--json'] + // THEN + const result = await fixture.runShopifyCommand(argv) + expect(result.exitCode).toBe(1) + expect(result.stdout).toBe('') + // Line wrapping is incidental; the filename and complete diagnostic are not. + expect(text(normalizeText(result.stderr, fixture, ['shopify.app.toml']))).toBe( + `Validation errors in /project/shopify.app.toml: ${diagnostic}`, + ) + expect(result.after['shopify.app.toml']).toBe(result.before['shopify.app.toml']) + }, + ) + }) + + describe('additional file and source boundaries', () => { + test('a dotenv directory is not loaded as an environment file', async ({fixture}) => { + // GIVEN + await fixture.reserve('dotenv-directory') + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout)).not.toHaveProperty('dotenv') + }) + + test('symlinked hidden configuration reads and writes only its fixture-local target', async ({fixture}) => { + // GIVEN + await fixture.reserve('hidden-symlink') + // WHEN + const flags: string[] = [] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(text(result.stderr)).toContain('Dev store symlink.myshopify.com') + expect(JSON.parse(await fixture.readFile('hidden-target.json'))[clientId]).toEqual({ + dev_store_url: 'symlink.myshopify.com', + }) + expect(result.after['.shopify/project.json']).toBe('symlink:../hidden-target.json') + }) + + test('existing type output that is a directory causes a thrown write/read failure', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-output-directory') + mockUiSpecification(fixture) + mockErrorReport(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(result.exitCode).toBe(1) + expect(text(result.stderr)).toContain('EISDIR') + expect((await stat(fixture.path('extensions/ui/shopify.d.ts'))).isDirectory()).toBe(true) + expect(operations(result).filter(Boolean)).toEqual(linkedOperations) + expect(result.requests.filter((request) => request.url === errorReportingUrl)).toHaveLength(1) + expect(changedPaths(result.rootBefore, result.rootAfter)).toEqual( + [storeRelativePath(fixture, 'cli-kit'), 'project/.shopify/.gitignore', 'project/.shopify/project.json'].sort(), + ) + expect(result.after['extensions/ui/shopify.extension.toml']).toBe( + result.before['extensions/ui/shopify.extension.toml'], + ) + }) + + test('only an ancestor tsconfig skips UI type generation', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-ancestor-tsconfig') + mockUiSpecification(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(result.after).not.toHaveProperty('extensions/ui/shopify.d.ts') + }) + + test('imported source contributes declarations without another main module', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-imported-source') + mockUiSpecification(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + await expect(fixture.readFile('extensions/ui/shopify.d.ts')).resolves.toContain('./src/helper') + }) + + test('UID insertion is skipped when another component has collected errors', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-errors-no-uid') + mockUiSpecification(fixture) + const configuration = (await fixture.readFile('extensions/ui/shopify.extension.toml')).replace(/^uid = .*\n/m, '') + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(2) + await expect(fixture.readFile('extensions/ui/shopify.extension.toml')).resolves.toBe(configuration) + expect(result.after).toHaveProperty('extensions/ui/shopify.d.ts') + }) + + test.for([ + {fixtureName: 'template-name-only', contents: 'name = "Legacy seed"\n'}, + {fixtureName: 'template-root-scopes', contents: 'scopes = "read_products"\n'}, + {fixtureName: 'template-hybrid-client-id', contents: 'client_id = ""\nscopes = "read_products"\n'}, + {fixtureName: 'template-current', contents: 'client_id = ""\n[access_scopes]\nscopes = "read_products"\n'}, + ])( + 'historical template $fixtureName enters linking rather than being reported as a linked app', + async ({fixtureName, contents}, {fixture}) => { + // GIVEN + await fixture.reserve(fixtureName) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.requests).toEqual([]) + expect(text(result.stderr)).toContain('app config link') + await expect(fixture.readFile('shopify.app.toml')).resolves.toBe(contents) + }, + ) + + test('unsupported extension directories do not trigger build/deploy locale reads', async ({fixture}) => { + // GIVEN + await fixture.reserve('localized-module-wrong-locale-location') + mockLocalizedSpecification(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(result.stderr).toBe('') + }) + }) + + describe('additional extension and mode contracts', () => { + test.for([ + {fixtureName: 'theme-extension', type: 'theme'}, + {fixtureName: 'function-extension', type: 'function'}, + {fixtureName: 'flow-action-extension', type: 'flow_action'}, + ])('reports $type without performing deploy-only work', async ({fixtureName, type}, {fixture}) => { + // GIVEN + await fixture.reserve(fixtureName) + fixture.remote.specifications.push(specification(type, 'extension')) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + expect(JSON.parse(result.stdout).allExtensions).toHaveLength(1) + expect(result.events.filter((event) => event.type === 'spawn')).toEqual([]) + }) + + test.for([[], ['--json'], ['--web-env'], ['--web-env', '--json']])( + 'collected web errors retain output then exit 2 in mode %j', + async (flags, {fixture}) => { + // GIVEN + await fixture.reserve('empty-web-config') + // WHEN + const argv = flags + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...argv]) + expect(result.exitCode).toBe(2) + if (flags.includes('--web-env')) { + expect(result.stderr).toBe('') + if (flags.includes('--json')) { + expect(JSON.parse(result.stdout)).toEqual({ + SHOPIFY_API_KEY: clientId, + SHOPIFY_API_SECRET: 'synthetic-app-secret', + SCOPES: 'read_products', + }) + } else { + expect(result.stdout).toBe( + `\n SHOPIFY_API_KEY=${clientId}\n SHOPIFY_API_SECRET=synthetic-app-secret\n SCOPES=read_products\n \n`, + ) + } + } else if (flags.includes('--json')) { + const report = JSON.parse(result.stdout) + expect(report.configuration.client_id).toBe(clientId) + expect(report.webs).toEqual([]) + expect(JSON.stringify(report.errors)).toContain('commands') + expect(result.stderr).toBe('') + } else { + expect(result.stdout).toBe('') + expect(text(result.stderr)).toContain('CURRENT APP CONFIGURATION') + expect(text(result.stderr)).toContain('App name Remote fixture') + expect(text(result.stderr)).toContain('DIRECTORY COMPONENTS') + expect(text(result.stderr)).toContain('TOOLING AND SYSTEM') + expect(result.stderr).not.toContain('Fixture web') + } + expect(operations(result)).toEqual(linkedOperations) + }, + ) + + test.for([[], ['--json'], ['--web-env'], ['--web-env', '--json']])( + 'thrown generation failure prevents the report in mode %j', + async (flags, {fixture}) => { + // GIVEN + await fixture.reserve('ui-no-installed-package') + mockUiSpecification(fixture) + // WHEN + const argv = flags + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...argv]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(result.stderr).not.toContain('CURRENT APP CONFIGURATION') + expect(text(result.stderr)).toContain( + 'Type reference for admin.product-details.action.render could not be found', + ) + expect(result.after).not.toHaveProperty('extensions/ui/shopify.d.ts') + }, + ) + }) + + describe('notifications, versions, and controlled subprocesses', () => { + test.for(['info', 'warning', 'error'])( + 'cached %s notification is displayed and obeys its exit boundary', + async (type, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + const store = await fixture.readStore('cli-kit') + await fixture.seedState({ + caches: { + ...(store.cache as object), + 'notifications-https://cdn.shopify.com/static/cli/notifications.json': { + value: JSON.stringify({ + notifications: [ + { + id: 'fixture-notice', + title: 'Fixture notice', + message: 'Fixture notification body', + type, + frequency: 'once', + ownerChannel: 'fixture', + }, + ], + }), + timestamp: now, + }, + }, + }) + // WHEN + const terminal = {replies: []} + // THEN + const result = await fixture.runShopifyCommand(['app', 'info'], {terminal}) + expect(result.exitCode === 0).toBe(type !== 'error') + expect(text(result.terminalOutput)).toContain('Fixture notification body') + expect(text(result.terminalOutput).includes('CURRENT APP CONFIGURATION')).toBe(type !== 'error') + }, + ) + + test.for([ + {value: 'true', shown: false}, + {value: 'y', shown: true}, + ])('early notification JSON reader differs for $value', async ({value, shown}, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.configure({ + environment: { + SHOPIFY_FLAG_JSON: value, + }, + }) + const store = await fixture.readStore('cli-kit') + await fixture.seedState({ + caches: { + ...(store.cache as object), + 'notifications-https://cdn.shopify.com/static/cli/notifications.json': { + value: JSON.stringify({ + notifications: [ + { + id: 'fixture-notice', + message: 'Fixture notification body', + type: 'info', + frequency: 'always', + ownerChannel: 'fixture', + }, + ], + }), + timestamp: now, + }, + }, + }) + // WHEN + const terminal = {replies: []} + // THEN + const result = await fixture.runShopifyCommand(['app', 'info'], {terminal}) + expect(result.exitCode).toBe(0) + expect(result.terminalOutput.includes('Fixture notification body')).toBe(shown) + }) + + test('duplicate CLI installations warn using a scripted version probe', async ({fixture}) => { + // GIVEN + await fixture.reserve('local-cli-dependency') + fixture.mockSubprocess({command: 'npm', args: ['list', '@shopify/cli'], stdout: '@shopify/cli@4.0.0\n'}) + // WHEN + const flags: string[] = [] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + expect(result.events.filter((event) => event.type === 'spawn')).toHaveLength(1) + expect(result.stderr).toContain('4.0.0') + }) + + test.for([false, true])( + 'forced upgrade uses scripted installation and version verification (failure=%s)', + async (failure, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.configure({ + environment: { + SHOPIFY_CLI_FORCE_AUTO_UPGRADE: '1', + }, + }) + const store = await fixture.readStore('cli-kit') + await fixture.seedState({ + caches: {...(store.cache as object), 'npm-package-@shopify/cli': {value: '4.999.0', timestamp: now}}, + }) + fixture.mockNetwork({ + method: 'GET', + url: 'https://cdn.shopify.com/static/cli/notifications.json', + responses: [{body: {notifications: []}}], + }) + fixture.mockSubprocess({ + command: 'npm', + args: ['install', '-g', '@shopify/cli@latest'], + exitCode: failure ? 1 : 0, + }) + if (!failure) + fixture.mockSubprocess({ + command: 'shopify', + args: [], + stdout: '@shopify/cli/4.999.0 darwin-arm64 node-v22.0.0\n', + }) + // WHEN + const flags: string[] = [] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + expect(text(result.stderr)).toContain('CURRENT APP CONFIGURATION') + expect( + result.events.some((event) => event.type === 'spawn' && event.args?.includes('@shopify/cli@latest')), + ).toBe(true) + expect(result.stderr.includes('Shopify CLI upgraded')).toBe(!failure) + if (failure) expect(text(result.stderr)).toContain('Version 4.999.0 available! Run') + expect(result.requests.some((request) => request.url === errorReportingUrl)).toBe(false) + }, + ) + + test('fresh blocking auto-upgrade notification prevents the install subprocess', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.configure({ + environment: { + SHOPIFY_CLI_FORCE_AUTO_UPGRADE: '1', + }, + }) + await fixture.seedState({ + caches: {'npm-package-@shopify/cli': {value: '4.999.0', timestamp: now}}, + }) + fixture.mockNetwork({ + method: 'GET', + url: 'https://cdn.shopify.com/static/cli/notifications.json', + responses: [ + { + body: { + notifications: [ + { + id: 'upgrade-block', + message: 'Do not upgrade', + type: 'error', + surface: 'autoupgrade', + frequency: 'always', + ownerChannel: 'fixture', + }, + ], + }, + }, + ], + }) + // WHEN + const flags: string[] = [] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(result.events.filter((event) => event.type === 'spawn')).toEqual([]) + }) + }) + + describe('report contracts', () => { + test.for([ + {fixtureName: 'report-components', exitCode: 0, extensionDetails: 'metafields 2'}, + { + fixtureName: 'ui-missing-source', + exitCode: 2, + extensionDetails: `error ! Couldn't find extensions/ui/src/index.ts +Please check the module path for admin.product-details.action.render + +Please check the configuration in +/project/extensions/ui/shopify.extension.toml`, + }, + ])( + 'renders the complete text report for $fixtureName', + async ({fixtureName, exitCode, extensionDetails}, {fixture}) => { + // GIVEN + await fixture.reserve(fixtureName) + mockUiSpecification(fixture) + // WHEN + const argv = ['app', 'info', '--no-color'] + // THEN + const result = await fixture.runShopifyCommand(argv) + expect(result.exitCode, result.stderr).toBe(exitCode) + expect(result.stdout).toBe('') + expect(result.stderr).toBe(stripVTControlCharacters(result.stderr)) + expect(normalizeText(result.stderr, fixture, ['extensions/ui/shopify.extension.toml'])).toBe( + `CURRENT APP CONFIGURATION + +Configuration file shopify.app.toml +App name Remote fixture +Client ID ${clientId} +Organization Fixture organization (123) +Access scopes read_products +Dev store Not yet configured +Update URLs Not yet configured +User fixture@example.test + +💡 To change these, run \`shopify app config link\` + +YOUR PROJECT + +Root location /project + +DIRECTORY COMPONENTS + +web +📂 web +📂 Fixture web web +roles frontend, backend + +ui_extension +📂 fixture-ui extensions/ui +config file shopify.extension.toml +${extensionDetails} + +TOOLING AND SYSTEM + +Shopify CLI 4.8.0 +Package manager pnpm +OS +Shell /bin/bash +Node version `, + ) + }, + ) + + test('reports extension identity, configuration and paths without runtime schemas', async ({fixture}) => { + // GIVEN + await fixture.reserve('report-components') + mockUiSpecification(fixture) + // WHEN + const argv = ['app', 'info', '--json'] + // THEN + const result = await fixture.runShopifyCommand(argv) + expect(result.exitCode, result.stderr).toBe(0) + expect(result.stderr).toBe('') + const report = JSON.parse(result.stdout) + expect(report).not.toHaveProperty('configSchema') + for (const specification of report.specifications) expect(specification).not.toHaveProperty('schema') + for (const extension of [...report.allExtensions, ...report.realExtensions]) { + expect(extension.specification).not.toHaveProperty('schema') + } + expect(report.allExtensions).toHaveLength(1) + const [extension] = report.allExtensions + const targeting = [{target: 'admin.product-details.action.render', module: './src/index.ts'}] + const metafields = [ + {namespace: 'fixture', key: 'first'}, + {namespace: 'fixture', key: 'second'}, + ] + expect(extension).toMatchObject({ + handle: 'fixture-ui', + uid: '11111111-1111-1111-1111-111111111111', + directory: fixture.path('extensions/ui'), + configurationPath: fixture.path('extensions/ui/shopify.extension.toml'), + specification: {identifier: 'ui_extension', experience: 'extension'}, + }) + expect(extension.configuration).toMatchObject({ + name: 'Fixture UI', + type: 'ui_extension', + handle: 'fixture-ui', + uid: '11111111-1111-1111-1111-111111111111', + api_version: '2025-10', + }) + expect(extension.configuration.targeting).toEqual(targeting) + expect(extension.configuration.metafields).toEqual(metafields) + // Compare reported target data, not duplicated deploy-step/asset metadata. + expect(extension.configuration.extension_points).toMatchObject([{...targeting[0], metafields}]) + expect(report.errors).toEqual({errors: []}) + }) + + test('reports a missing project before authentication or project writes', async ({fixture}) => { + // GIVEN + await fixture.reserve('no-project') + const kitBefore = await fixture.readStore('cli-kit') + // WHEN + const argv = ['app', 'info', '--json'] + // THEN + const result = await fixture.runShopifyCommand(argv) + expect(result.exitCode).toBe(1) + expect(result.stdout).toBe('') + expect(text(result.stderr)).toContain('Could not find a Shopify app configuration file') + expect(result.stderr).toContain(fixture.projectPath) + expect(result.requests).toEqual([]) + expect(result.after).toEqual(result.before) + expect(changedPaths(result.rootBefore, result.rootAfter)).toEqual([storeRelativePath(fixture, 'cli-kit')]) + await expect(fixture.readStore('cli-kit')).resolves.toMatchObject(kitBefore) + }) + }) + + describe('environment-only flag bindings', () => { + test('selects a named config from the environment without argv or a saved preference', async ({fixture}) => { + // GIVEN + await fixture.reserve('named-config-dotenv') + fixture.configure({environment: {SHOPIFY_FLAG_APP_CONFIG: 'staging'}}) + // WHEN + const argv = ['app', 'info', '--json'] + // THEN + const result = await fixture.runShopifyCommand(argv) + expect(result.exitCode, result.stderr).toBe(0) + expect(JSON.parse(result.stdout)).toMatchObject({ + configuration: {name: 'Staging fixture'}, + dotenv: {variables: {SHARED: 'staging'}}, + }) + expect(result.stdout).not.toContain('DEFAULT_ONLY') + expect(result.stderr).toBe('') + }) + + test('selects the project from the path environment variable', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.configure({cwd: fixture.root, environment: {SHOPIFY_FLAG_PATH: fixture.projectPath}}) + // WHEN + const argv = ['app', 'info', '--json'] + // THEN + const result = await fixture.runShopifyCommand(argv) + expect(result.exitCode, result.stderr).toBe(0) + expect(JSON.parse(result.stdout).directory).toBe(fixture.projectPath) + expect(result.stderr).toBe('') + }) + + test('overrides the client ID from the environment without rewriting the TOML', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.configure({environment: {SHOPIFY_FLAG_CLIENT_ID: 'environment-client'}}) + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + variables: {apiKey: 'environment-client'}, + responses: [{body: {data: {app: {...fixture.remote.app, key: 'environment-client'}}}}], + }) + // WHEN + const argv = ['app', 'info', '--json'] + // THEN + const result = await fixture.runShopifyCommand(argv) + expect(result.exitCode, result.stderr).toBe(0) + expect(JSON.parse(result.stdout).configuration.client_id).toBe('environment-client') + expect(result.after['shopify.app.toml']).toBe(result.before['shopify.app.toml']) + expect(result.stderr).toBe('') + }) + + test('an environment alias overrides another saved account for this invocation only', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + const other = structuredClone(fixture.user) + other.identity.userId = 'other' + other.identity.alias = 'other' + other.applications[businessAudience].accessToken = 'other-account-token' + await fixture.seedState({ + authentication: { + currentSessionId: 'other', + sessions: {'accounts.shopify.com': {'synthetic-user': fixture.user, other}}, + }, + }) + fixture.configure({environment: {SHOPIFY_FLAG_AUTH_ALIAS: 'fixture'}}) + // WHEN + const argv = ['app', 'info'] + // THEN + const result = await fixture.runShopifyCommand(argv) + expect(result.exitCode, result.stderr).toBe(0) + expect(text(result.stderr)).toContain('User fixture@example.test') + expect(result.requests[0]!.headers?.authorization).toBe('Bearer synthetic-business-token') + expect((await fixture.readStore('cli-kit')).currentSessionId).toBe('other') + }) + + test('selects web-env output from the environment', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.configure({environment: {SHOPIFY_FLAG_OUTPUT_WEB_ENV: '1'}}) + // WHEN + const argv = ['app', 'info', '--json'] + // THEN + const result = await fixture.runShopifyCommand(argv) + expect(result.exitCode).toBe(0) + expect(result.stderr).toBe('') + expect(JSON.parse(result.stdout)).toEqual({ + SHOPIFY_API_KEY: clientId, + SHOPIFY_API_SECRET: 'synthetic-app-secret', + SCOPES: 'read_products', + }) + }) + + test.for(['argv', 'environment'])('no-color overrides forced color via %s', async (source, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.configure({environment: {FORCE_COLOR: '1'}}) + const colored = await fixture.runShopifyCommand(['app', 'info'], {terminal: {replies: []}}) + if (source === 'environment') fixture.configure({environment: {SHOPIFY_FLAG_NO_COLOR: '1'}}) + // WHEN + const argv = ['app', 'info', ...(source === 'argv' ? ['--no-color'] : [])] + // THEN + const result = await fixture.runShopifyCommand(argv, {terminal: {replies: []}}) + expect(result.exitCode).toBe(0) + const sgr = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`) + expect(colored.terminalOutput).toMatch(sgr) + expect(result.terminalOutput).not.toMatch(sgr) + expect(normalizeText(result.terminalOutput, fixture)).toBe(normalizeText(colored.terminalOutput, fixture)) + }) + }) + + describe('result modes and parsing', () => { + test.for(['--json', '-j'])('returns loaded local data on stdout with %s', async (flag, {fixture}) => { + // GIVEN + await fixture.reserve('dotenv-secret') + // WHEN + const flags = [flag] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(result.stderr).toBe('') + const report = JSON.parse(result.stdout) + expect(report).toMatchObject({ + name: 'Local fixture', + directory: fixture.projectPath, + configPath: fixture.path('shopify.app.toml'), + packageManager: 'pnpm', + usesWorkspaces: false, + }) + expect(report.configuration).toEqual({ + client_id: clientId, + name: 'Local fixture', + application_url: 'https://fixture.example.test', + embedded: true, + access_scopes: {scopes: 'read_products'}, + auth: {redirect_urls: ['https://fixture.example.test/auth/callback']}, + }) + expect(report.webs).toEqual([ + { + directory: fixture.path('web'), + configuration: {name: 'Fixture web', roles: ['frontend', 'backend'], commands: {dev: 'node server.js'}}, + framework: 'unknown', + }, + ]) + expect(report.dotenv).toEqual({path: fixture.path('.env'), variables: {SYNTHETIC_SECRET: 'fixture-secret'}}) + expect(report.organization).toEqual({id: '123', businessName: 'Fixture organization'}) + expect(report.nodeDependencies).toEqual({}) + expect(report.allExtensions).toEqual([]) + expect(report.errors).toEqual({errors: []}) + expect(report).not.toHaveProperty('configSchema') + expect(result.stdout).not.toContain('synthetic-app-secret') + expect(operations(result)).toEqual(linkedOperations) + }) + + test.for([false, true])('reports web environment (json=%s)', async (json, {fixture}) => { + // GIVEN + await fixture.reserve('dotenv-web-secret') + // WHEN + const flags = ['--web-env', ...(json ? ['--json'] : [])] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(result.stderr).toBe('') + if (json) + expect(JSON.parse(result.stdout)).toEqual({ + SHOPIFY_API_KEY: clientId, + SHOPIFY_API_SECRET: 'synthetic-app-secret', + SCOPES: 'read_products', + }) + else { + expect(result.stdout).toBe( + `\n SHOPIFY_API_KEY=${clientId}\n SHOPIFY_API_SECRET=synthetic-app-secret\n SCOPES=read_products\n \n`, + ) + } + expect(result.stdout).not.toContain('not-the-remote-secret') + expect(operations(result)).toEqual(linkedOperations) + }) + + test.for([false, true])('handles an app with no secrets (json=%s)', async (json, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [ + { + body: { + data: { + app: { + ...fixture.remote.app, + activeRoot: {...fixture.remote.app.activeRoot, clientCredentials: {secrets: []}}, + }, + }, + }, + }, + ], + }) + // WHEN + const flags = ['--web-env', ...(json ? ['--json'] : [])] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(result.stderr).toBe('') + if (json) expect(JSON.parse(result.stdout)).toEqual({SHOPIFY_API_KEY: clientId, SCOPES: 'read_products'}) + else + expect(result.stdout).toBe( + `\n SHOPIFY_API_KEY=${clientId}\n SHOPIFY_API_SECRET=\n SCOPES=read_products\n \n`, + ) + }) + + test('uses only the first rotated secret', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.remote.app.activeRoot.clientCredentials.secrets.push({key: 'second-secret'}) + // WHEN + const flags = ['--web-env', '--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(result.stderr).toBe('') + expect(JSON.parse(result.stdout)).toEqual({ + SHOPIFY_API_KEY: clientId, + SHOPIFY_API_SECRET: 'synthetic-app-secret', + SCOPES: 'read_products', + }) + expect(result.stdout).not.toContain('second-secret') + }) + + test('help does not authenticate or load malformed project files', async ({fixture}) => { + // GIVEN + await fixture.reserve('invalid-configs-help') + // WHEN + const flags = ['--help'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(result.stdout + result.stderr).toContain('--web-env') + expect(result.requests).toEqual([]) + expect(result.after).toEqual(result.before) + }) + + test.for([ + {flags: ['--environment', 'production'], message: /Nonexistent flag.*--environment/}, + {flags: ['--json=false'], message: /Unexpected argument.*false/}, + {flags: ['--json', 'false'], message: /Unexpected argument.*false/}, + {flags: ['--no-json'], message: /Nonexistent flag.*--no-json/}, + {flags: ['--config', 'one', '--config', 'two'], message: /--config.*specified.*once/}, + {flags: ['--config', 'staging', '--reset'], message: /--config.*--reset|--reset.*--config/}, + {flags: ['--config', 'staging', '--client-id', 'other'], message: /--config.*--client-id|--client-id.*--config/}, + {flags: ['--unknown'], message: /Nonexistent flag.*--unknown/}, + {flags: ['unexpected-argument'], message: /command .*unexpected-argument.*not found/i}, + ])('rejects invalid argv $flags before API work', async ({flags, message}, {fixture}) => { + // GIVEN + await fixture.reserve('dotenv-unused-during-parse-failure') + // WHEN + const argv = flags + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...argv]) + expect(result.exitCode).toBe(flags[0] === 'unexpected-argument' ? 1 : 2) + expect(text(result.stderr)).toMatch(message) + expect(result.requests).toEqual([]) + expect(result.stdout).toBe('') + // Unknown subcommands stop earlier than app-info's parser/metadata hooks. + const expectedChanges = + flags[0] === 'unexpected-argument' + ? [] + : [ + storeRelativePath(fixture, 'cli-kit'), + 'project/.shopify/.gitignore', + 'project/.shopify/project.json', + ].sort() + expect(changedPaths(result.rootBefore, result.rootAfter)).toEqual(expectedChanges) + if (flags[0] !== 'unexpected-argument') + expect(JSON.parse(await fixture.readFile('.shopify/project.json'))).toEqual({}) + }) + + test.for(['1', 'true', 'TRUE', 'yes', 'y'])('reads JSON flag binding %s', async (value, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.configure({ + environment: { + SHOPIFY_FLAG_JSON: value, + }, + }) + // WHEN + const flags: string[] = [] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout).configuration.client_id).toBe(clientId) + }) + + test('a false reset environment value still conflicts with explicit config', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.configure({ + environment: { + SHOPIFY_FLAG_RESET: 'false', + }, + }) + // WHEN + const flags = ['--config', 'staging'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.requests).toEqual([]) + expect(text(result.stderr)).toMatch(/reset.*config|config.*reset/) + }) + + test.for(['argv', 'environment'])( + 'verbose via %s logs requests without changing the JSON result', + async (source, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + const ordinary = await fixture.runShopifyCommand(['app', 'info', '--json']) + fixture.configure({ + environment: { + DEBUG: '', + SHOPIFY_FLAG_VERBOSE: source === 'environment' ? '1' : undefined, + }, + }) + // WHEN + const flags = ['--json', ...(source === 'argv' ? ['--verbose'] : [])] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(result.stdout.startsWith('{'), result.stdout.slice(0, 800)).toBe(true) + expect(JSON.parse(result.stdout).configuration.client_id).toBe(clientId) + expect(JSON.parse(result.stdout)).toEqual(JSON.parse(ordinary.stdout)) + expect(result.stderr).toContain('Running command app info') + expect(result.stderr).toContain('ActiveAppReleaseFromApiKey') + expect(operations(result)).toEqual(['ActiveAppReleaseFromApiKey', 'fetchSpecifications']) + }, + ) + }) + + describe('configuration selection and stored preference', () => { + test('saved configuration selects named TOML and dotenv without merging defaults', async ({fixture}) => { + // GIVEN + await fixture.reserve('named-config-dotenv') + await fixture.seedState({ + appPreferences: {configFile: 'shopify.app.staging.toml', title: 'Stale title', orgId: '999'}, + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout)).toMatchObject({ + configuration: {name: 'Staging fixture'}, + dotenv: {variables: {SHARED: 'staging'}}, + }) + expect(JSON.parse(result.stdout).dotenv.variables).not.toHaveProperty('DEFAULT_ONLY') + const store = await fixture.readStore('app') + expect(store[fixture.projectPath]).toMatchObject({ + configFile: 'shopify.app.staging.toml', + title: 'Remote fixture', + orgId: '123', + }) + }) + + test.for(['--config', '-c'])( + 'explicit %s overrides but does not persist a saved selection', + async (flag, {fixture}) => { + // GIVEN + await fixture.reserve('named-staging-fixture') + fixture.configure({ + environment: { + SHOPIFY_FLAG_APP_CONFIG: 'missing', + }, + }) + await fixture.seedState({ + appPreferences: {configFile: 'shopify.app.toml'}, + }) + // WHEN + const flags = [flag, 'staging', '--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout).configuration.name).toBe('Staging fixture') + expect((await fixture.readStore('app'))[fixture.projectPath]).toMatchObject({configFile: 'shopify.app.toml'}) + }, + ) + + test('client-ID override changes the request without choosing another TOML', async ({fixture}) => { + // GIVEN + await fixture.reserve('client-id-override') + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + variables: {apiKey: 'overridden-client-id'}, + responses: [{body: {data: {app: {...fixture.remote.app, key: 'overridden-client-id'}}}}], + }) + // WHEN + const flags = ['--client-id', 'overridden-client-id', '--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout).configuration).toMatchObject({ + name: 'Local fixture', + client_id: 'overridden-client-id', + }) + await expect(fixture.readFile('shopify.app.toml')).resolves.toContain(`client_id = "${clientId}"`) + }) + + test('recovers a stale preference when exactly one configuration remains', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + await fixture.seedState({ + appPreferences: {configFile: 'shopify.app.deleted.toml'}, + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(result.stderr).toContain('shopify.app.deleted.toml') + expect((await fixture.readStore('app'))[fixture.projectPath]).toMatchObject({configFile: 'shopify.app.toml'}) + }) + + test('does not invent a default when only a named config exists', async ({fixture}) => { + // GIVEN + await fixture.reserve('named-config-only') + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(result.requests).toEqual([]) + }) + + test.for(['--path', 'INIT_CWD'])('selects the project through %s', async (source, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + if (source === 'INIT_CWD') + fixture.configure({ + environment: { + INIT_CWD: fixture.projectPath, + }, + }) + // WHEN + const flags = source === '--path' ? ['--path', fixture.projectPath, '--json'] : ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags], {cwd: fixture.root}) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout).directory).toBe(fixture.projectPath) + }) + + test.for([ + {fixtureName: 'invalid-app-toml-syntax', message: /Unterminated inline array/}, + {fixtureName: 'invalid-app-client-id-type', message: /app config link.*requires additional flags/}, + ])('rejects invalid app TOML $fixtureName before remote loading', async ({fixtureName, message}, {fixture}) => { + // GIVEN + await fixture.reserve(fixtureName) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(result.requests).toEqual([]) + expect(result.exitCode).toBe(1) + expect(text(result.stderr)).toMatch(message) + expect(result.after).toEqual(result.before) + }) + }) + + describe('local files and package metadata', () => { + test('dotenv parsing retains empty values, last duplicates, and unexpanded variables', async ({fixture}) => { + // GIVEN + await fixture.reserve('dotenv-parsing') + // eslint-disable-next-line no-template-curly-in-string -- Deliberately unexpanded dotenv data. + const reference = '${DUP}' + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout).dotenv.variables).toEqual({DUP: 'last', EMPTY: '', REFERENCE: reference}) + }) + + test('a named configuration without dotenv does not fall back to .env', async ({fixture}) => { + // GIVEN + await fixture.reserve('named-config-no-dotenv') + // WHEN + const flags = ['--config', 'staging', '--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(result.stdout).not.toContain('NOT_SELECTED') + }) + + test('nearest empty environments file shadows a malformed ancestor', async ({fixture}) => { + // GIVEN + await fixture.reserve('environments-shadowing') + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(operations(result)).toEqual(linkedOperations) + }) + + test('malformed nearest environments file fails before API calls', async ({fixture}) => { + // GIVEN + await fixture.reserve('environments-invalid') + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.requests).toEqual([]) + expect(result.stdout).toBe('') + }) + + test('migrates a legacy hidden dev store while preserving unrelated data and ignore bytes', async ({fixture}) => { + // GIVEN + await fixture.reserve('hidden-legacy') + // WHEN + const flags: string[] = [] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(text(result.stderr)).toContain('Dev store legacy.myshopify.com') + expect(JSON.parse(await fixture.readFile('.shopify/project.json'))).toEqual({ + dev_store_url: 'legacy.myshopify.com', + unrelated: true, + [clientId]: {dev_store_url: 'legacy.myshopify.com'}, + }) + await expect(fixture.readFile('.shopify/.gitignore')).resolves.toBe('preserve-this\n') + }) + + test('an empty matching hidden entry suppresses legacy migration', async ({fixture}) => { + // GIVEN + await fixture.reserve('hidden-empty-matching-entry') + // WHEN + const flags: string[] = [] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(text(result.stderr)).toContain('Dev store Not yet configured') + expect(result.stderr).not.toContain('legacy.myshopify.com') + }) + + test('malformed hidden JSON is ignored without repairing its bytes', async ({fixture}) => { + // GIVEN + await fixture.reserve('hidden-invalid-json') + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout)._hiddenConfig).toEqual({}) + await expect(fixture.readFile('.shopify/project.json')).resolves.toBe('{bad') + }) + + test.for([ + {fixtureName: 'package-manager-yarn', marker: 'yarn.lock', manager: 'yarn'}, + {fixtureName: 'package-manager-pnpm', marker: 'pnpm-lock.yaml', manager: 'pnpm'}, + {fixtureName: 'package-manager-bun', marker: 'bun.lock', manager: 'bun'}, + {fixtureName: 'package-manager-npm', marker: 'package-lock.json', manager: 'npm'}, + ])('detects $manager from $marker without running it', async ({fixtureName, marker, manager}, {fixture}) => { + // GIVEN + await fixture.reserve(fixtureName) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout).packageManager).toBe(manager) + expect(result.events.filter((event) => event.type === 'spawn')).toEqual([]) + }) + + test('manifest absence skips marker detection', async ({fixture}) => { + // GIVEN + await fixture.reserve('missing-manifest') + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout)).toMatchObject({ + packageManager: 'unknown', + nodeDependencies: {}, + usesWorkspaces: false, + }) + }) + + test('devDependencies override dependencies; an empty workspace array is truthy', async ({fixture}) => { + // GIVEN + await fixture.reserve('manifest-dependencies-workspaces') + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout)).toMatchObject({nodeDependencies: {example: '2'}, usesWorkspaces: true}) + expect(JSON.parse(result.stdout).nodeDependencies).not.toHaveProperty('peer') + }) + + test.for([ + {fixtureName: 'manifest-invalid-json', contents: '{bad', message: /JSON.*position|property name/}, + {fixtureName: 'manifest-null', contents: 'null', message: /null.*dependencies/}, + {fixtureName: 'manifest-bom', contents: '\ufeff{}', message: /Unexpected token/}, + ])('malformed manifest $fixtureName aborts', async ({fixtureName, contents, message}, {fixture}) => { + // GIVEN + await fixture.reserve(fixtureName) + if (contents === 'null') + fixture.mockNetwork({ + method: 'POST', + url: 'https://error-analytics-production.shopifysvc.com/', + responses: [{status: 200, body: {}}], + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(result.exitCode).toBe(1) + expect(text(result.stderr)).toMatch(message) + expect(result.after).toEqual(result.before) + }) + + test('no web files is a valid project', async ({fixture}) => { + // GIVEN + await fixture.reserve('no-web') + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout).webs).toEqual([]) + }) + + test('legacy web type normalizes to roles without executing commands', async ({fixture}) => { + // GIVEN + await fixture.reserve('legacy-web-type') + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout).webs[0].configuration.roles).toEqual(['backend']) + expect(result.events.filter((event) => event.type === 'spawn')).toEqual([]) + }) + + test.for([{fixtureName: 'web-missing-command'}, {fixtureName: 'web-invalid-command'}])( + 'invalid web contents $fixtureName yield a report then exit 2', + async ({fixtureName}, {fixture}) => { + // GIVEN + await fixture.reserve(fixtureName) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(2) + expect(JSON.parse(result.stdout).webs).toEqual([]) + expect(operations(result)).toEqual(linkedOperations) + }, + ) + + test('duplicate web roles across files are collected errors', async ({fixture}) => { + // GIVEN + await fixture.reserve('duplicate-web-roles') + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(2) + expect(JSON.parse(result.stdout).webs).toHaveLength(2) + }) + }) + + describe('authentication and remote data', () => { + test('known account alias reuses credentials and preserves the saved account', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + const before = await fixture.readStore('cli-kit') + // WHEN + const flags = ['--auth-alias', 'fixture'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(text(result.stderr)).toContain('User fixture@example.test') + expect(operations(result)).toEqual(linkedOperations) + expect((await fixture.readStore('cli-kit')).sessionStore).toBe(before.sessionStore) + expect((await fixture.readStore('cli-kit')).currentSessionId).toBe('synthetic-user') + }) + + test('unknown alias fails even with an automation token', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.configure({ + environment: { + SHOPIFY_APP_AUTOMATION_TOKEN: 'synthetic-automation-token', + }, + }) + // WHEN + const flags = ['--auth-alias', 'missing'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(text(result.stderr)).toContain('No authenticated account found for alias missing') + expect(result.requests).toEqual([]) + }) + + test('invalid serialized sessions are removed before alias lookup fails', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + await fixture.seedState({ + authentication: { + serializedSessions: '{invalid', + }, + }) + // WHEN + const flags = ['--auth-alias', 'fixture'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + const store = await fixture.readStore('cli-kit') + expect(store).not.toHaveProperty('sessionStore') + expect(store).not.toHaveProperty('currentSessionId') + expect(store.autoUpgradeEnabled).toBe(false) + expect(result.requests).toEqual([]) + }) + + test('successful account response with no user renders unknown, not an authentication retry', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.mockNetwork({ + operation: 'UserInfo', + responses: [{body: {data: {currentUserAccount: null}}}], + }) + // WHEN + const flags: string[] = [] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(text(result.stderr)).toContain('User unknown') + expect(operations(result)).toEqual(linkedOperations) + }) + + test('missing remote app fails before organization/specification requests', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [{body: {data: {app: null}}}], + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(text(result.stderr)).toContain(`No app with client ID \`${clientId}\` found`) + expect(operations(result)).toEqual(linkedOperations.slice(0, 2)) + }) + + test('missing organization fails without a fallback organization', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.mockNetwork({ + operation: 'FindOrganization', + responses: [{body: {data: {currentUserAccount: {organization: null}}}}], + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(operations(result)).toEqual(linkedOperations.slice(0, 3)) + }) + + test.for([403, 500])('HTTP %s fails without a generic status retry', async (status, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [{status, body: {errors: [{message: 'Fixture API failure'}]}}], + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(operations(result)).toEqual(linkedOperations.slice(0, 2)) + }) + + test('GraphQL errors reject partial data even with HTTP 200', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [ + { + body: {data: {app: fixture.remote.app}, errors: [{message: 'Fixture GraphQL failure'}]}, + }, + ], + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(text(result.stderr)).toContain('Fixture GraphQL failure') + }) + + test('throttling retries remain active with network-level retries disabled', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [ + { + status: 429, + headers: {'Retry-After': '0'}, + body: {errors: [{message: 'Throttled', extensions: {code: 'THROTTLED'}}]}, + }, + {body: {data: {app: fixture.remote.app}}}, + ], + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(operations(result)).toEqual([ + 'UserInfo', + 'ActiveAppReleaseFromApiKey', + 'ActiveAppReleaseFromApiKey', + 'FindOrganization', + 'fetchSpecifications', + ]) + }) + + test('throttling stops after ten retries rather than looping forever', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [ + ...Array.from({length: 11}, () => ({ + status: 429, + headers: {'Retry-After': '0'}, + body: {errors: [{message: 'Throttled', extensions: {code: 'THROTTLED'}}]}, + })), + ], + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(operations(result).filter((operation) => operation === 'ActiveAppReleaseFromApiKey')).toHaveLength(11) + expect(operations(result)).not.toContain('FindOrganization') + }) + + test('a second process renders cached identity and organization alongside fresh app data', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + await fixture.runShopifyCommand(['app', 'info', '--json']) + const freshApp = structuredClone(fixture.remote.app) + freshApp.activeRelease.version.name = 'Updated remote app' + fixture.mockNetwork({operation: 'ActiveAppReleaseFromApiKey', responses: [{body: {data: {app: freshApp}}}]}) + fixture.mockNetwork({ + operation: 'UserInfo', + responses: [{status: 500, body: {errors: [{message: 'Must use cached user'}]}}], + }) + fixture.mockNetwork({ + operation: 'FindOrganization', + responses: [{status: 500, body: {errors: [{message: 'Must use cached organization'}]}}], + }) + // WHEN + const flags: string[] = [] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(operations(result)).toEqual(['ActiveAppReleaseFromApiKey', 'fetchSpecifications']) + expect(result.stdout).toBe('') + expect(text(result.stderr)).toContain('App name Updated remote app') + expect(text(result.stderr)).toContain('User fixture@example.test') + expect(text(result.stderr)).toContain('Organization Fixture organization (123)') + expect((await fixture.readStore('app'))[fixture.projectPath]).toMatchObject({title: 'Updated remote app'}) + }) + + test.for([ + {fixtureName: 'unlinked-named-template', reset: false}, + {fixtureName: 'linked-app', reset: true}, + ])( + 'noninteractive linking requires a client ID before requests (reset=$reset)', + async ({fixtureName, reset}, {fixture}) => { + // GIVEN + await fixture.reserve(fixtureName) + // WHEN + const flags = reset ? ['--reset'] : [] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(text(result.stderr)).toContain('requires additional flags in non-interactive terminal environments') + expect(result.requests).toEqual([]) + }, + ) + }) + + describe('session refresh and specification failures', () => { + test('refreshes an expired identity, exchanges four audiences, and saves the new session', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + const user = structuredClone(fixture.user) + user.identity.expiresAt = '2000-01-01T00:00:00Z' + await fixture.seedState({ + authentication: { + sessions: { + 'accounts.shopify.com': { + 'synthetic-user': user, + }, + }, + }, + }) + fixture.mockNetwork({ + method: 'POST', + url: 'https://accounts.shopify.com/oauth/token', + form: {grant_type: 'refresh_token'}, + responses: [ + { + body: { + access_token: 'synthetic-refreshed-identity', + refresh_token: 'synthetic-refreshed-refresh', + expires_in: 3600, + scope: user.identity.scopes.join(' '), + }, + }, + ], + }) + fixture.mockTokenExchange(appAudience, 'proactively-refreshed-app-token') + fixture.mockTokenExchange(businessAudience, 'proactively-refreshed-business-token') + for (const request of fixture.networkMocks.filter((entry) => entry.operation)) { + request.headers = { + Authorization: `Bearer ${ + request.url === businessPlatformUrl + ? 'proactively-refreshed-business-token' + : 'proactively-refreshed-app-token' + }`, + } + } + fixture.mockTokenExchange('ee139b3d-5861-4d45-b387-1bc3ada7811c', 'synthetic-storefront-token') + fixture.mockTokenExchange( + '271e16d403dfa18082ffb3d197bd2b5f4479c3fc32736d69296829cbb28d41a6', + 'synthetic-partners-token', + ) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + expect(result.requests).toHaveLength(9) + expect(operations(result).filter(Boolean)).toEqual(linkedOperations) + const store = await fixture.readStore('cli-kit') + expect(JSON.parse(store.sessionStore as string)['accounts.shopify.com']['synthetic-user']).toMatchObject({ + identity: {accessToken: 'synthetic-refreshed-identity', refreshToken: 'synthetic-refreshed-refresh'}, + applications: { + [appAudience]: {accessToken: 'proactively-refreshed-app-token'}, + [businessAudience]: {accessToken: 'proactively-refreshed-business-token'}, + }, + }) + const exchanges = result.requests.filter((request) => new URLSearchParams(request.body).has('audience')) + expect(exchanges).toHaveLength(4) + for (const exchange of exchanges) + expect(new URLSearchParams(exchange.body).get('subject_token')).toBe('synthetic-refreshed-identity') + expect(JSON.parse(result.stdout).configuration.client_id).toBe(clientId) + }) + + test('automation credentials exchange two tokens and report a service account instead of the stored user', async ({ + fixture, + }) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.configure({ + environment: { + SHOPIFY_APP_AUTOMATION_TOKEN: 'synthetic-automation-token', + }, + }) + fixture.mockTokenExchange(appAudience, 'synthetic-app-token') + fixture.mockTokenExchange(businessAudience, 'synthetic-business-token') + const before = await fixture.readStore('cli-kit') + // WHEN + const flags: string[] = [] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + expect(result.requests).toHaveLength(6) + expect(text(result.stderr)).toContain('Service account Fixture organization') + expect(new URLSearchParams(result.requests[0]!.body).get('subject_token')).toBe('synthetic-automation-token') + expect((await fixture.readStore('cli-kit')).sessionStore).toBe(before.sessionStore) + }) + + test('expired identity attempts refresh; invalid_request clears the session and aborts', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + const user = structuredClone(fixture.user) + user.identity.expiresAt = '2000-01-01T00:00:00Z' + await fixture.seedState({ + authentication: { + sessions: { + 'accounts.shopify.com': { + 'synthetic-user': user, + }, + }, + }, + }) + fixture.mockNetwork({ + method: 'POST', + url: 'https://accounts.shopify.com/oauth/token', + responses: [{status: 400, body: {error: 'invalid_request', error_description: 'Fixture refresh rejection'}}], + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(result.requests).toHaveLength(1) + expect(new URLSearchParams(result.requests[0]!.body).get('grant_type')).toBe('refresh_token') + await expect(fixture.readStore('cli-kit')).resolves.not.toHaveProperty('sessionStore') + }) + + test('missing identity scopes starts device authorization rather than reusing API tokens', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + const user = structuredClone(fixture.user) + user.identity.scopes = ['openid'] + await fixture.seedState({ + authentication: { + sessions: { + 'accounts.shopify.com': { + 'synthetic-user': user, + }, + }, + }, + }) + fixture.mockNetwork({ + method: 'POST', + url: 'https://accounts.shopify.com/oauth/device_authorization', + responses: [{status: 400, body: {error: 'invalid_client'}}], + }) + fixture.mockNetwork({ + method: 'POST', + url: 'https://error-analytics-production.shopifysvc.com/', + responses: [{body: {}}], + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(result.requests.map((request) => request.url)).toEqual([ + 'https://accounts.shopify.com/oauth/device_authorization', + 'https://error-analytics-production.shopifysvc.com/', + ]) + expect(text(result.stderr)).toContain('Failed to start authorization process') + }) + + test('tokens exactly at the four-minute threshold are still reusable', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + const user = structuredClone(fixture.user) + user.identity.expiresAt = new Date(now + 4 * 60 * 1000).toISOString() + await fixture.seedState({ + authentication: { + sessions: { + 'accounts.shopify.com': { + 'synthetic-user': user, + }, + }, + }, + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(operations(result)).toEqual(linkedOperations) + }) + + test('empty specification results do not fall back to local extension specifications', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-extension') + mockUiSpecification(fixture) + fixture.remote.specifications.splice(0) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + expect(JSON.parse(result.stdout).allExtensions).toEqual([]) + expect(result.after).not.toHaveProperty('extensions/ui/shopify.d.ts') + }) + + test('a changed extension contract is fetched after cache warmup and appears in the report', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-extension') + mockUiSpecification(fixture) + await fixture.runShopifyCommand(['app', 'info', '--json']) + fixture.remote.specifications.splice( + 3, + 1, + specification('ui_extension', 'extension', { + type: 'object', + required: ['fixture_required_field'], + }), + ) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(2) + expect(result.stdout).toContain('fixture_required_field') + expect(operations(result)).toEqual(['ActiveAppReleaseFromApiKey', 'fetchSpecifications']) + }) + }) + + describe('extension dependencies and generated types', () => { + test('resolves installed exports and generates shopify.d.ts during info', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-extension') + mockUiSpecification(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + expect(JSON.parse(result.stdout).allExtensions).toHaveLength(1) + await expect(fixture.readFile('extensions/ui/shopify.d.ts')).resolves.toContain( + "import('@shopify/ui-extensions/admin.product-details.action.render').Api", + ) + expect(operations(result)).toEqual(linkedOperations) + }) + + test.for([{fixtureName: 'ui-old-api-stale-types'}, {fixtureName: 'ui-no-tsconfig-stale-types'}])( + 'skips installed-package resolution with a closed generation gate $fixtureName', + async ({fixtureName}, {fixture}) => { + // GIVEN + await fixture.reserve(fixtureName) + mockUiSpecification(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + expect(result.stderr).toBe('') + await expect(fixture.readFile('extensions/ui/shopify.d.ts')).resolves.toBe('stale declarations\n') + }, + ) + + test('identical generated declarations are not rewritten', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-extension') + mockUiSpecification(fixture) + await fixture.runShopifyCommand(['app', 'info', '--json']) + const output = fixture.path('extensions/ui/shopify.d.ts') + const oldTime = new Date('2000-01-01T00:00:00Z') + await utimes(output, oldTime, oldTime) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect((await stat(output)).mtime.getTime()).toBe(oldTime.getTime()) + expect(result.after['extensions/ui/shopify.d.ts']).toBe(result.before['extensions/ui/shopify.d.ts']) + }) + + test('installed ShopifyGlobal re-exports change generated declarations', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-shopify-global') + mockUiSpecification(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + await expect(fixture.readFile('extensions/ui/shopify.d.ts')).resolves.toContain('.ShopifyGlobal') + }) + + test('a declared dependency cannot replace a missing installed target export', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-missing-target-export') + mockUiSpecification(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(text(result.stderr)).toContain('Type reference for admin.product-details.action.render could not be found') + }) + + test('successful tools compilation still aborts if its helper export is absent', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-missing-helper-export') + mockUiSpecification(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(text(result.stderr)).toContain('could not be found') + }) + + test('extension UID insertion and generated types are observable writes', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-no-uid') + mockUiSpecification(fixture) + + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + await expect(fixture.readFile('extensions/ui/shopify.extension.toml')).resolves.toMatch(/uid = "[^"]+"/) + expect(result.after).toHaveProperty('extensions/ui/shopify.d.ts') + expect(result.after['extensions/ui/shopify.extension.toml']).not.toBe( + result.before['extensions/ui/shopify.extension.toml'], + ) + }) + + test('missing source module is a collected error rather than a dependency abort', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-missing-source') + mockUiSpecification(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(2) + expect(JSON.parse(result.stdout).allExtensions).toHaveLength(1) + expect(result.after).not.toHaveProperty('extensions/ui/shopify.d.ts') + }) + + test('successful tools compilation adds tool types', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-tools-valid') + mockUiSpecification(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + const declarations = await fixture.readFile('extensions/ui/shopify.d.ts') + expect(declarations).toBe(`import '@shopify/ui-extensions'; + +//@ts-ignore +declare module './src/index.ts' { + interface LookupInput { + id?: string; + [k: string]: unknown; + } + + type LookupOutput = unknown; + interface ShopifyTools { + /** + * Find a fixture + */ + register( + name: 'lookup', + handler: (input: LookupInput) => LookupOutput | Promise, + ): () => void; + } + + const shopify: import('@shopify/ui-extensions/admin').WithGeneratedTools< + import('@shopify/ui-extensions/admin.product-details.action.render').Api, + ShopifyTools + >; + const globalThis: { shopify: typeof shopify }; +} +`) + expect(result.stderr).toBe('') + }) + + test.for([ + {fixtureName: 'ui-tools-missing-file', warning: false}, + {fixtureName: 'ui-tools-empty', warning: false}, + {fixtureName: 'ui-tools-invalid-json', warning: true}, + {fixtureName: 'ui-tools-invalid-shape', warning: true}, + {fixtureName: 'ui-tools-duplicate-names', warning: true}, + ])( + 'tools file state $fixtureName preserves base types and its warning boundary', + async ({fixtureName, warning}, {fixture}) => { + // GIVEN + await fixture.reserve(fixtureName) + mockUiSpecification(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + expect(JSON.parse(result.stdout).allExtensions).toHaveLength(1) + const declarations = await fixture.readFile('extensions/ui/shopify.d.ts') + expect(declarations).toContain("import('@shopify/ui-extensions/admin.product-details.action.render').Api") + expect(declarations).not.toContain('ShopifyTools') + if (warning) { + expect(text(result.stderr)).toContain( + fixtureName === 'ui-tools-invalid-shape' + ? 'Invalid tools definition' + : 'Failed to create tools type definition', + ) + expect(result.stderr).toContain('tools.json') + } else { + expect(result.stderr).toBe('') + } + }, + ) + + test.for([ + {fixtureName: 'ui-intent-missing-file', message: 'was not found. Skipping intent type generation'}, + {fixtureName: 'ui-intent-invalid-json', message: 'Failed to create intent type definition'}, + {fixtureName: 'ui-intent-invalid-shape', message: 'Invalid intent schema'}, + ])( + 'missing/invalid intent schema $fixtureName warns without stopping the report', + async ({fixtureName, message}, {fixture}) => { + // GIVEN + await fixture.reserve(fixtureName) + mockUiSpecification(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + expect(text(result.stderr)).toContain(message) + expect(result.stderr).toContain('intent.json') + expect(JSON.parse(result.stdout).allExtensions).toHaveLength(1) + const declarations = await fixture.readFile('extensions/ui/shopify.d.ts') + expect(declarations).toContain("import('@shopify/ui-extensions/admin.product-details.action.render').Api") + expect(declarations).not.toContain('ShopifyGeneratedIntentVariants') + }, + ) + + test('valid intent schemas generate input, value, and output declarations', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-intent-valid') + mockUiSpecification(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + const declarations = await fixture.readFile('extensions/ui/shopify.d.ts') + expect(declarations).toBe(`import '@shopify/ui-extensions'; + +//@ts-ignore +declare module './src/index.ts' { + interface EditFixtureIntentInput { + inputValue?: string; + [k: string]: unknown; + } + + interface EditFixtureIntentValue { + selectedValue?: number; + [k: string]: unknown; + } + + type EditFixtureIntentOutput = boolean; + + interface EditFixtureIntentRequest { + action: 'edit'; + type: 'fixture'; + data: EditFixtureIntentInput; + value?: EditFixtureIntentValue; + } + + type ShopifyGeneratedIntentVariants = + import('@shopify/ui-extensions/admin').ShopifyGeneratedIntentVariant< + EditFixtureIntentRequest, + EditFixtureIntentOutput + >; + + const shopify: import('@shopify/ui-extensions/admin').WithGeneratedIntents< + import('@shopify/ui-extensions/admin.product-details.action.render').Api, + ShopifyGeneratedIntentVariants + >; + const globalThis: { shopify: typeof shopify }; +} +`) + expect(result.stderr).toBe('') + }) + + test('duplicate valid intent identities abort instead of warning and skipping', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-duplicate-intents') + mockUiSpecification(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(text(result.stderr)).toContain('Intent "edit:fixture" is defined multiple times') + }) + + test('tools JSON external HTTP references use the intercepted native-fetch transport', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-http-schema-reference') + mockUiSpecification(fixture) + fixture.mockNetwork({ + method: 'GET', + url: 'https://schemas.example.test/input.json', + responses: [{body: {type: 'object', properties: {externalValue: {type: 'string'}}}}], + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + expect( + result.requests.filter((request) => request.url === 'https://schemas.example.test/input.json'), + ).toHaveLength(1) + await expect(fixture.readFile('extensions/ui/shopify.d.ts')).resolves.toContain('externalValue') + expect(result.stderr).toBe('') + }) + + test('local schema references use the process cwd rather than the tools directory', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-local-schema-reference') + mockUiSpecification(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + await expect(fixture.readFile('extensions/ui/shopify.d.ts')).resolves.toContain('rootValue') + expect(operations(result)).toEqual(linkedOperations) + }) + + test('a failing external schema reference warns and skips tools, not the report', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-http-schema-failure') + mockUiSpecification(fixture) + fixture.mockNetwork({ + method: 'GET', + url: 'https://schemas.example.test/missing.json', + responses: [{status: 404, text: 'missing'}], + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + expect(text(result.stderr)).toContain('Failed to create tools type definition') + await expect(fixture.readFile('extensions/ui/shopify.d.ts')).resolves.not.toContain('ShopifyTools') + }) + + test('a missing target specification becomes a collected extension error', async ({fixture}) => { + // GIVEN + await fixture.reserve('ui-extension') + mockUiSpecification(fixture) + fixture.remote.specifications.splice(3) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(2) + expect(result.stdout).not.toBe('') + expect(result.after).not.toHaveProperty('extensions/ui/shopify.d.ts') + }) + }) + + describe('remote-defined localized configuration modules', () => { + test.for([ + {fixtureName: 'localized-module', contents: undefined}, + {fixtureName: 'locales-valid', contents: '{"title":"Fixture"}'}, + {fixtureName: 'locales-not-json', contents: 'not JSON but valid UTF-8'}, + ])('accepts absent or nonempty UTF-8 locale bytes $fixtureName', async ({fixtureName, contents}, {fixture}) => { + // GIVEN + await fixture.reserve(fixtureName) + mockLocalizedSpecification(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).toBe(0) + expect(JSON.parse(result.stdout).configuration.fixture_configuration).toEqual({enabled: true}) + if (contents !== undefined) await expect(fixture.readFile('locales/en.default.json')).resolves.toBe(contents) + }) + + test.for([ + {fixtureName: 'locales-no-default', message: 'Missing default language'}, + {fixtureName: 'locales-multiple-defaults', message: 'default'}, + {fixtureName: 'locales-empty', message: 'empty'}, + ])('rejects locale file state $fixtureName before rendering', async ({fixtureName, message}, {fixture}) => { + // GIVEN + await fixture.reserve(fixtureName) + mockLocalizedSpecification(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode, result.stderr).not.toBe(0) + expect(result.stdout).toBe('') + expect(text(result.stderr).toLowerCase()).toContain(message.toLowerCase()) + }) + + test('invalid UTF-8 locale bytes abort loading', async ({fixture}) => { + // GIVEN + await fixture.reserve('locales-invalid-utf8') + mockLocalizedSpecification(fixture) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).not.toBe(0) + expect(result.stdout).toBe('') + expect(text(result.stderr)).toContain('UTF-8') + }) + + test('unrelated root locales are not validated when no localized configuration spec is active', async ({ + fixture, + }) => { + // GIVEN + await fixture.reserve('inactive-locales') + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(result.stderr).toBe('') + }) + }) + + describe('fixture safety boundaries', () => { + test('unregistered schema requests fail the test even when type generation catches their errors', async ({ + fixture, + }) => { + // GIVEN + await fixture.reserve('ui-unregistered-schema') + mockUiSpecification(fixture) + // WHEN + const flags = ['--json'] + // THEN + const command = fixture.runShopifyCommand(['app', 'info', ...flags]) + await expect(command).rejects.toThrow('Unregistered request: GET https://unregistered.example.test/schema.json') + }) + + test('unregistered background version lookup fails even if the command otherwise succeeds', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + await fixture.seedState({ + caches: {}, + }) + // WHEN + const flags = ['--json'] + // THEN + const command = fixture.runShopifyCommand(['app', 'info', ...flags]) + await expect(command).rejects.toThrow('registry.npmjs.org') + }) + }) + + describe('deprecation and lifecycle output', () => { + test('runtime locale controls the date fragment without translating the surrounding warning', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.configure({ + environment: { + LANG: 'fr_FR.UTF-8', + }, + }) + fixture.configure({ + environment: { + LC_ALL: 'fr_FR.UTF-8', + }, + }) + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [ + { + body: { + data: {app: fixture.remote.app}, + extensions: {deprecations: [{supportedUntilDate: '2030-01-01T00:00:00Z'}]}, + }, + }, + ], + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(text(result.stderr)).toContain('Upgrade to the latest CLI version by 1 janvier 2030.') + }) + + test('prints the earliest future deprecation deadline after the text report', async ({fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [ + { + body: { + data: {app: fixture.remote.app}, + extensions: { + deprecations: [ + {supportedUntilDate: '2031-01-01T00:00:00Z'}, + {supportedUntilDate: '2030-01-01T00:00:00Z'}, + ], + }, + }, + }, + ], + }) + // WHEN + const flags: string[] = [] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(text(result.stderr)).toContain('Upgrade to the latest CLI version by January 1, 2030.') + expect(result.stderr.indexOf('CURRENT APP CONFIGURATION')).toBeLessThan( + result.stderr.indexOf('Upgrade to the latest CLI'), + ) + }) + + test.for([ + {zone: 'UTC', date: 'January 1, 2030'}, + {zone: 'America/Los_Angeles', date: 'December 31, 2029'}, + ])('formats a future deadline in $zone after the report', async ({zone, date}, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.configure({ + environment: { + TZ: zone, + }, + }) + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [ + { + body: { + data: {app: fixture.remote.app}, + extensions: {deprecations: [{supportedUntilDate: '2030-01-01T00:00:00Z'}]}, + }, + }, + ], + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout).configuration.client_id).toBe(clientId) + expect(text(result.stderr)).toContain(`Upgrade to the latest CLI version by ${date}.`) + expect(result.events.filter((event) => event.type === 'spawn')).toEqual([]) + }) + + test.for(['2000-01-01T00:00:00Z', new Date(now).toISOString(), 'invalid-date'])( + 'ignores non-future deprecation deadline %s', + async (date, {fixture}) => { + // GIVEN + await fixture.reserve('linked-app') + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [ + { + body: {data: {app: fixture.remote.app}, extensions: {deprecations: [{supportedUntilDate: date}]}}, + }, + ], + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(0) + expect(result.stderr).toBe('') + }, + ) + + test('direct exit 2 does not run the deprecation postrun hook', async ({fixture}) => { + // GIVEN + await fixture.reserve('empty-web-config') + fixture.mockNetwork({ + operation: 'ActiveAppReleaseFromApiKey', + responses: [ + { + body: { + data: {app: fixture.remote.app}, + extensions: {deprecations: [{supportedUntilDate: '2030-01-01T00:00:00Z'}]}, + }, + }, + ], + }) + // WHEN + const flags = ['--json'] + // THEN + const result = await fixture.runShopifyCommand(['app', 'info', ...flags]) + expect(result.exitCode).toBe(2) + expect(JSON.parse(result.stdout).webs).toEqual([]) + expect(result.stderr).not.toContain('Upgrade to the latest CLI') + }) + }) +}) diff --git a/test/fixtures/.gitignore b/test/fixtures/.gitignore new file mode 100644 index 00000000000..a0fdff7cbbb --- /dev/null +++ b/test/fixtures/.gitignore @@ -0,0 +1,3 @@ +# These are intentional synthetic inputs, including dotenv files and package stubs. +!**/ +!**/* diff --git a/test/fixtures/app-information/README.md b/test/fixtures/app-information/README.md new file mode 100644 index 00000000000..a65ba0d3c9d --- /dev/null +++ b/test/fixtures/app-information/README.md @@ -0,0 +1,55 @@ + + +# App information filesystem scenarios + +Each subdirectory provides one complete filesystem input for `fixture.reserve(name)`: + +```text +scenario-name/ +├── project/ +│ ├── shopify.app.toml +│ ├── package.json +│ └── web/ +│ └── shopify.web.toml +└── shopify.environments.toml # Optional ancestor input. +``` + +The catalog contains 116 filesystem scenarios. Tests with identical file state reuse a scenario, but each test receives its own temporary copy. The fixture doesn't apply a baseline overlay. If a file is absent from the scenario, it is absent from the copied project. + +## Common scenarios + +| Scenario | File state | +| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| `linked-app` | Valid linked configuration, manifest, pnpm marker, and one web component | +| `report-components` | Web and UI components, including metafields, for complete report expectations | +| `no-project` | Empty project directory for discovery failure | +| `linked-legacy-*`, `linked-endpoint-*`, `linked-uri-*`, `linked-old-*`, `linked-required-*`, `linked-mixed-*` | Historical linked compatibility inputs and validation boundaries | +| `web-*` | Ordered union, field normalization, port/HMR validation, and framework inputs | +| `named-staging-fixture` | Default and named staging configurations | +| `named-config-dotenv` | Named configuration plus distinct default/named dotenv files | +| `config-name-collision` | Existing configurations used by interactive rename/overwrite tests | +| `template-*` | Historical or current unlinked template seeds | +| `hidden-*` | `.shopify/project.json` shapes, malformed data, or a relative symlink | +| `package-manager-*`, `manifest-*` | Marker detection and manifest states | +| `ui-extension` | Remote-DOM UI extension, source, tsconfig, and synthetic installed exports | +| `ui-tools-*`, `ui-intent-*` | Supporting JSON contents and missing/invalid states | +| `localized-module`, `locales-*` | Remote-defined configuration-module inputs and root locale bytes | + +## Rules + +- Treat source fixtures as immutable. Tests modify only the reserved copy. +- Keep inputs synthetic, including dotenv values, client IDs, and package stubs. +- Preserve exact bytes. Some JSON, TOML, UTF-8, and declaration files are intentionally invalid. +- `.gitkeep` preserves deliberately empty directories. It is an ordinary, irrelevant file in the copied fixture. +- Symlinks must be relative and stay inside their scenario. Copies preserve the link text rather than pointing back into this catalog. +- Do not put sessions or platform-specific home directories here. Use `seedState()` for authentication, preferences, and caches; runtime paths are not portable fixture bytes. +- The harness's `bin`, home/store/temp paths, protocol files, and outer module-resolution guard are reserved. Ancestor app inputs such as an environments file or lockfile can live beside `project/`. +- Keep network responses, clocks, terminal interaction, and subprocess outcomes explicit in test setup. Reserving files does not infer remote extension specifications from local TOML. + +The fixture tree is excluded from Nx project discovery, TypeScript checking, ESLint, and Prettier. Its local Git-ignore override makes intentional `.env`, declaration files, and synthetic `node_modules` contents visible to Git. Do not run package installation inside these fixture directories. + +When adding a scenario, copy a suitable directory, change only the relevant inputs, and reserve it by name in the test. Reuse a scenario when only flags or network responses differ. Use `writeFile()` only when bytes depend on a runtime value, such as a loopback server port, or when a test deliberately changes files between command executions. diff --git a/test/fixtures/app-information/client-id-override/project/package.json b/test/fixtures/app-information/client-id-override/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/client-id-override/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/client-id-override/project/pnpm-lock.yaml b/test/fixtures/app-information/client-id-override/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/client-id-override/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/client-id-override/project/shopify.app.other.toml b/test/fixtures/app-information/client-id-override/project/shopify.app.other.toml new file mode 100644 index 00000000000..93a8ac43c7d --- /dev/null +++ b/test/fixtures/app-information/client-id-override/project/shopify.app.other.toml @@ -0,0 +1,10 @@ +client_id = "overridden-client-id" +name = "Other configuration" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/client-id-override/project/shopify.app.toml b/test/fixtures/app-information/client-id-override/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/client-id-override/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/client-id-override/project/web/shopify.web.toml b/test/fixtures/app-information/client-id-override/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/client-id-override/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/config-name-collision/project/package.json b/test/fixtures/app-information/config-name-collision/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/config-name-collision/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/config-name-collision/project/pnpm-lock.yaml b/test/fixtures/app-information/config-name-collision/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/config-name-collision/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/config-name-collision/project/shopify.app.remote-fixture.toml b/test/fixtures/app-information/config-name-collision/project/shopify.app.remote-fixture.toml new file mode 100644 index 00000000000..de3362f9c69 --- /dev/null +++ b/test/fixtures/app-information/config-name-collision/project/shopify.app.remote-fixture.toml @@ -0,0 +1,10 @@ +client_id = "other-client" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/config-name-collision/project/shopify.app.toml b/test/fixtures/app-information/config-name-collision/project/shopify.app.toml new file mode 100644 index 00000000000..de3362f9c69 --- /dev/null +++ b/test/fixtures/app-information/config-name-collision/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "other-client" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/config-name-collision/project/web/shopify.web.toml b/test/fixtures/app-information/config-name-collision/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/config-name-collision/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/dotenv-directory/project/.env/.gitkeep b/test/fixtures/app-information/dotenv-directory/project/.env/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/fixtures/app-information/dotenv-directory/project/package.json b/test/fixtures/app-information/dotenv-directory/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/dotenv-directory/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/dotenv-directory/project/pnpm-lock.yaml b/test/fixtures/app-information/dotenv-directory/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/dotenv-directory/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/dotenv-directory/project/shopify.app.toml b/test/fixtures/app-information/dotenv-directory/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/dotenv-directory/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/dotenv-directory/project/web/shopify.web.toml b/test/fixtures/app-information/dotenv-directory/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/dotenv-directory/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/dotenv-parsing/project/.env b/test/fixtures/app-information/dotenv-parsing/project/.env new file mode 100644 index 00000000000..8c6f0b07cfe --- /dev/null +++ b/test/fixtures/app-information/dotenv-parsing/project/.env @@ -0,0 +1,4 @@ +DUP=first +DUP=last +EMPTY= +REFERENCE=${DUP} diff --git a/test/fixtures/app-information/dotenv-parsing/project/package.json b/test/fixtures/app-information/dotenv-parsing/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/dotenv-parsing/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/dotenv-parsing/project/pnpm-lock.yaml b/test/fixtures/app-information/dotenv-parsing/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/dotenv-parsing/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/dotenv-parsing/project/shopify.app.toml b/test/fixtures/app-information/dotenv-parsing/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/dotenv-parsing/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/dotenv-parsing/project/web/shopify.web.toml b/test/fixtures/app-information/dotenv-parsing/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/dotenv-parsing/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/dotenv-secret/project/.env b/test/fixtures/app-information/dotenv-secret/project/.env new file mode 100644 index 00000000000..176fd9a64a8 --- /dev/null +++ b/test/fixtures/app-information/dotenv-secret/project/.env @@ -0,0 +1 @@ +SYNTHETIC_SECRET=fixture-secret diff --git a/test/fixtures/app-information/dotenv-secret/project/package.json b/test/fixtures/app-information/dotenv-secret/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/dotenv-secret/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/dotenv-secret/project/pnpm-lock.yaml b/test/fixtures/app-information/dotenv-secret/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/dotenv-secret/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/dotenv-secret/project/shopify.app.toml b/test/fixtures/app-information/dotenv-secret/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/dotenv-secret/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/dotenv-secret/project/web/shopify.web.toml b/test/fixtures/app-information/dotenv-secret/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/dotenv-secret/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/dotenv-unused-during-parse-failure/project/.env b/test/fixtures/app-information/dotenv-unused-during-parse-failure/project/.env new file mode 100644 index 00000000000..d97495d4340 --- /dev/null +++ b/test/fixtures/app-information/dotenv-unused-during-parse-failure/project/.env @@ -0,0 +1 @@ +VALUE=not-used diff --git a/test/fixtures/app-information/dotenv-unused-during-parse-failure/project/package.json b/test/fixtures/app-information/dotenv-unused-during-parse-failure/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/dotenv-unused-during-parse-failure/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/dotenv-unused-during-parse-failure/project/pnpm-lock.yaml b/test/fixtures/app-information/dotenv-unused-during-parse-failure/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/dotenv-unused-during-parse-failure/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/dotenv-unused-during-parse-failure/project/shopify.app.toml b/test/fixtures/app-information/dotenv-unused-during-parse-failure/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/dotenv-unused-during-parse-failure/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/dotenv-unused-during-parse-failure/project/web/shopify.web.toml b/test/fixtures/app-information/dotenv-unused-during-parse-failure/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/dotenv-unused-during-parse-failure/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/dotenv-web-secret/project/.env b/test/fixtures/app-information/dotenv-web-secret/project/.env new file mode 100644 index 00000000000..a1494312f28 --- /dev/null +++ b/test/fixtures/app-information/dotenv-web-secret/project/.env @@ -0,0 +1 @@ +SHOPIFY_API_SECRET=not-the-remote-secret diff --git a/test/fixtures/app-information/dotenv-web-secret/project/package.json b/test/fixtures/app-information/dotenv-web-secret/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/dotenv-web-secret/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/dotenv-web-secret/project/pnpm-lock.yaml b/test/fixtures/app-information/dotenv-web-secret/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/dotenv-web-secret/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/dotenv-web-secret/project/shopify.app.toml b/test/fixtures/app-information/dotenv-web-secret/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/dotenv-web-secret/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/dotenv-web-secret/project/web/shopify.web.toml b/test/fixtures/app-information/dotenv-web-secret/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/dotenv-web-secret/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/duplicate-web-roles/project/another/shopify.web.toml b/test/fixtures/app-information/duplicate-web-roles/project/another/shopify.web.toml new file mode 100644 index 00000000000..6e786c18610 --- /dev/null +++ b/test/fixtures/app-information/duplicate-web-roles/project/another/shopify.web.toml @@ -0,0 +1,3 @@ +roles = ["frontend"] +[commands] +dev = "must-not-run" diff --git a/test/fixtures/app-information/duplicate-web-roles/project/package.json b/test/fixtures/app-information/duplicate-web-roles/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/duplicate-web-roles/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/duplicate-web-roles/project/pnpm-lock.yaml b/test/fixtures/app-information/duplicate-web-roles/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/duplicate-web-roles/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/duplicate-web-roles/project/shopify.app.toml b/test/fixtures/app-information/duplicate-web-roles/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/duplicate-web-roles/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/duplicate-web-roles/project/web/shopify.web.toml b/test/fixtures/app-information/duplicate-web-roles/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/duplicate-web-roles/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/empty-dev-store/project/.shopify/project.json b/test/fixtures/app-information/empty-dev-store/project/.shopify/project.json new file mode 100644 index 00000000000..54a50c6a699 --- /dev/null +++ b/test/fixtures/app-information/empty-dev-store/project/.shopify/project.json @@ -0,0 +1 @@ +{"synthetic-client-id":{"dev_store_url":"hidden.myshopify.com"}} \ No newline at end of file diff --git a/test/fixtures/app-information/empty-dev-store/project/package.json b/test/fixtures/app-information/empty-dev-store/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/empty-dev-store/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/empty-dev-store/project/pnpm-lock.yaml b/test/fixtures/app-information/empty-dev-store/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/empty-dev-store/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/empty-dev-store/project/shopify.app.toml b/test/fixtures/app-information/empty-dev-store/project/shopify.app.toml new file mode 100644 index 00000000000..eb51039d06b --- /dev/null +++ b/test/fixtures/app-information/empty-dev-store/project/shopify.app.toml @@ -0,0 +1,13 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] + +[build] +dev_store_url = "" diff --git a/test/fixtures/app-information/empty-dev-store/project/web/shopify.web.toml b/test/fixtures/app-information/empty-dev-store/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/empty-dev-store/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/empty-web-config/project/package.json b/test/fixtures/app-information/empty-web-config/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/empty-web-config/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/empty-web-config/project/pnpm-lock.yaml b/test/fixtures/app-information/empty-web-config/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/empty-web-config/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/empty-web-config/project/shopify.app.toml b/test/fixtures/app-information/empty-web-config/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/empty-web-config/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/empty-web-config/project/web/shopify.web.toml b/test/fixtures/app-information/empty-web-config/project/web/shopify.web.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/fixtures/app-information/environments-invalid/project/package.json b/test/fixtures/app-information/environments-invalid/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/environments-invalid/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/environments-invalid/project/pnpm-lock.yaml b/test/fixtures/app-information/environments-invalid/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/environments-invalid/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/environments-invalid/project/shopify.app.toml b/test/fixtures/app-information/environments-invalid/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/environments-invalid/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/environments-invalid/project/shopify.environments.toml b/test/fixtures/app-information/environments-invalid/project/shopify.environments.toml new file mode 100644 index 00000000000..3abc6718882 --- /dev/null +++ b/test/fixtures/app-information/environments-invalid/project/shopify.environments.toml @@ -0,0 +1 @@ +invalid = [ \ No newline at end of file diff --git a/test/fixtures/app-information/environments-invalid/project/web/shopify.web.toml b/test/fixtures/app-information/environments-invalid/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/environments-invalid/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/environments-shadowing/project/package.json b/test/fixtures/app-information/environments-shadowing/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/environments-shadowing/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/environments-shadowing/project/pnpm-lock.yaml b/test/fixtures/app-information/environments-shadowing/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/environments-shadowing/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/environments-shadowing/project/shopify.app.toml b/test/fixtures/app-information/environments-shadowing/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/environments-shadowing/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/environments-shadowing/project/shopify.environments.toml b/test/fixtures/app-information/environments-shadowing/project/shopify.environments.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/fixtures/app-information/environments-shadowing/project/web/shopify.web.toml b/test/fixtures/app-information/environments-shadowing/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/environments-shadowing/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/environments-shadowing/shopify.environments.toml b/test/fixtures/app-information/environments-shadowing/shopify.environments.toml new file mode 100644 index 00000000000..3abc6718882 --- /dev/null +++ b/test/fixtures/app-information/environments-shadowing/shopify.environments.toml @@ -0,0 +1 @@ +invalid = [ \ No newline at end of file diff --git a/test/fixtures/app-information/flow-action-extension/project/extensions/fixture/locales/fr.json b/test/fixtures/app-information/flow-action-extension/project/extensions/fixture/locales/fr.json new file mode 100644 index 00000000000..4f80ad883c3 --- /dev/null +++ b/test/fixtures/app-information/flow-action-extension/project/extensions/fixture/locales/fr.json @@ -0,0 +1 @@ +invalid bytes for deploy \ No newline at end of file diff --git a/test/fixtures/app-information/flow-action-extension/project/extensions/fixture/shopify.extension.toml b/test/fixtures/app-information/flow-action-extension/project/extensions/fixture/shopify.extension.toml new file mode 100644 index 00000000000..9345fe93de8 --- /dev/null +++ b/test/fixtures/app-information/flow-action-extension/project/extensions/fixture/shopify.extension.toml @@ -0,0 +1,6 @@ +[[extensions]] +name = "Fixture" +handle = "fixture" +type = "flow_action" +uid = "fixture-uid" +runtime_url = "https://fixture.example.test/flow" diff --git a/test/fixtures/app-information/flow-action-extension/project/package.json b/test/fixtures/app-information/flow-action-extension/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/flow-action-extension/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/flow-action-extension/project/pnpm-lock.yaml b/test/fixtures/app-information/flow-action-extension/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/flow-action-extension/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/flow-action-extension/project/shopify.app.toml b/test/fixtures/app-information/flow-action-extension/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/flow-action-extension/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/flow-action-extension/project/web/shopify.web.toml b/test/fixtures/app-information/flow-action-extension/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/flow-action-extension/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/function-extension/project/extensions/fixture/locales/fr.json b/test/fixtures/app-information/function-extension/project/extensions/fixture/locales/fr.json new file mode 100644 index 00000000000..4f80ad883c3 --- /dev/null +++ b/test/fixtures/app-information/function-extension/project/extensions/fixture/locales/fr.json @@ -0,0 +1 @@ +invalid bytes for deploy \ No newline at end of file diff --git a/test/fixtures/app-information/function-extension/project/extensions/fixture/shopify.extension.toml b/test/fixtures/app-information/function-extension/project/extensions/fixture/shopify.extension.toml new file mode 100644 index 00000000000..9b671377a9b --- /dev/null +++ b/test/fixtures/app-information/function-extension/project/extensions/fixture/shopify.extension.toml @@ -0,0 +1,9 @@ +[[extensions]] +name = "Fixture" +handle = "fixture" +type = "function" +uid = "fixture-uid" +api_version = "2025-10" +[extensions.build] +command = "never-run" +path = "missing.wasm" diff --git a/test/fixtures/app-information/function-extension/project/package.json b/test/fixtures/app-information/function-extension/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/function-extension/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/function-extension/project/pnpm-lock.yaml b/test/fixtures/app-information/function-extension/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/function-extension/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/function-extension/project/shopify.app.toml b/test/fixtures/app-information/function-extension/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/function-extension/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/function-extension/project/web/shopify.web.toml b/test/fixtures/app-information/function-extension/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/function-extension/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/hidden-array/project/.shopify/project.json b/test/fixtures/app-information/hidden-array/project/.shopify/project.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/fixtures/app-information/hidden-array/project/.shopify/project.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-array/project/package.json b/test/fixtures/app-information/hidden-array/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/hidden-array/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-array/project/pnpm-lock.yaml b/test/fixtures/app-information/hidden-array/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/hidden-array/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/hidden-array/project/shopify.app.toml b/test/fixtures/app-information/hidden-array/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/hidden-array/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/hidden-array/project/web/shopify.web.toml b/test/fixtures/app-information/hidden-array/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/hidden-array/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/hidden-boolean/project/.shopify/project.json b/test/fixtures/app-information/hidden-boolean/project/.shopify/project.json new file mode 100644 index 00000000000..f32a5804e29 --- /dev/null +++ b/test/fixtures/app-information/hidden-boolean/project/.shopify/project.json @@ -0,0 +1 @@ +true \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-boolean/project/package.json b/test/fixtures/app-information/hidden-boolean/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/hidden-boolean/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-boolean/project/pnpm-lock.yaml b/test/fixtures/app-information/hidden-boolean/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/hidden-boolean/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/hidden-boolean/project/shopify.app.toml b/test/fixtures/app-information/hidden-boolean/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/hidden-boolean/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/hidden-boolean/project/web/shopify.web.toml b/test/fixtures/app-information/hidden-boolean/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/hidden-boolean/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/hidden-directory-collision/project/.shopify b/test/fixtures/app-information/hidden-directory-collision/project/.shopify new file mode 100644 index 00000000000..f1c2a916b98 --- /dev/null +++ b/test/fixtures/app-information/hidden-directory-collision/project/.shopify @@ -0,0 +1 @@ +not a directory \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-directory-collision/project/package.json b/test/fixtures/app-information/hidden-directory-collision/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/hidden-directory-collision/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-directory-collision/project/pnpm-lock.yaml b/test/fixtures/app-information/hidden-directory-collision/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/hidden-directory-collision/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/hidden-directory-collision/project/shopify.app.toml b/test/fixtures/app-information/hidden-directory-collision/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/hidden-directory-collision/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/hidden-directory-collision/project/web/shopify.web.toml b/test/fixtures/app-information/hidden-directory-collision/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/hidden-directory-collision/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/hidden-empty-matching-entry/project/.shopify/project.json b/test/fixtures/app-information/hidden-empty-matching-entry/project/.shopify/project.json new file mode 100644 index 00000000000..417404d93af --- /dev/null +++ b/test/fixtures/app-information/hidden-empty-matching-entry/project/.shopify/project.json @@ -0,0 +1 @@ +{"dev_store_url":"legacy.myshopify.com","synthetic-client-id":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-empty-matching-entry/project/package.json b/test/fixtures/app-information/hidden-empty-matching-entry/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/hidden-empty-matching-entry/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-empty-matching-entry/project/pnpm-lock.yaml b/test/fixtures/app-information/hidden-empty-matching-entry/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/hidden-empty-matching-entry/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/hidden-empty-matching-entry/project/shopify.app.toml b/test/fixtures/app-information/hidden-empty-matching-entry/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/hidden-empty-matching-entry/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/hidden-empty-matching-entry/project/web/shopify.web.toml b/test/fixtures/app-information/hidden-empty-matching-entry/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/hidden-empty-matching-entry/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/hidden-invalid-json/project/.shopify/project.json b/test/fixtures/app-information/hidden-invalid-json/project/.shopify/project.json new file mode 100644 index 00000000000..d3db885cc09 --- /dev/null +++ b/test/fixtures/app-information/hidden-invalid-json/project/.shopify/project.json @@ -0,0 +1 @@ +{bad \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-invalid-json/project/package.json b/test/fixtures/app-information/hidden-invalid-json/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/hidden-invalid-json/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-invalid-json/project/pnpm-lock.yaml b/test/fixtures/app-information/hidden-invalid-json/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/hidden-invalid-json/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/hidden-invalid-json/project/shopify.app.toml b/test/fixtures/app-information/hidden-invalid-json/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/hidden-invalid-json/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/hidden-invalid-json/project/web/shopify.web.toml b/test/fixtures/app-information/hidden-invalid-json/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/hidden-invalid-json/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/hidden-legacy/project/.shopify/.gitignore b/test/fixtures/app-information/hidden-legacy/project/.shopify/.gitignore new file mode 100644 index 00000000000..9dc8984bd0e --- /dev/null +++ b/test/fixtures/app-information/hidden-legacy/project/.shopify/.gitignore @@ -0,0 +1 @@ +preserve-this diff --git a/test/fixtures/app-information/hidden-legacy/project/.shopify/project.json b/test/fixtures/app-information/hidden-legacy/project/.shopify/project.json new file mode 100644 index 00000000000..3f1d368d1d2 --- /dev/null +++ b/test/fixtures/app-information/hidden-legacy/project/.shopify/project.json @@ -0,0 +1 @@ +{"dev_store_url":"legacy.myshopify.com","unrelated":true} \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-legacy/project/package.json b/test/fixtures/app-information/hidden-legacy/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/hidden-legacy/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-legacy/project/pnpm-lock.yaml b/test/fixtures/app-information/hidden-legacy/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/hidden-legacy/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/hidden-legacy/project/shopify.app.toml b/test/fixtures/app-information/hidden-legacy/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/hidden-legacy/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/hidden-legacy/project/web/shopify.web.toml b/test/fixtures/app-information/hidden-legacy/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/hidden-legacy/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/hidden-null/project/.shopify/project.json b/test/fixtures/app-information/hidden-null/project/.shopify/project.json new file mode 100644 index 00000000000..ec747fa47dd --- /dev/null +++ b/test/fixtures/app-information/hidden-null/project/.shopify/project.json @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-null/project/package.json b/test/fixtures/app-information/hidden-null/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/hidden-null/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-null/project/pnpm-lock.yaml b/test/fixtures/app-information/hidden-null/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/hidden-null/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/hidden-null/project/shopify.app.toml b/test/fixtures/app-information/hidden-null/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/hidden-null/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/hidden-null/project/web/shopify.web.toml b/test/fixtures/app-information/hidden-null/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/hidden-null/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/hidden-number/project/.shopify/project.json b/test/fixtures/app-information/hidden-number/project/.shopify/project.json new file mode 100644 index 00000000000..f70d7bba4ae --- /dev/null +++ b/test/fixtures/app-information/hidden-number/project/.shopify/project.json @@ -0,0 +1 @@ +42 \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-number/project/package.json b/test/fixtures/app-information/hidden-number/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/hidden-number/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-number/project/pnpm-lock.yaml b/test/fixtures/app-information/hidden-number/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/hidden-number/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/hidden-number/project/shopify.app.toml b/test/fixtures/app-information/hidden-number/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/hidden-number/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/hidden-number/project/web/shopify.web.toml b/test/fixtures/app-information/hidden-number/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/hidden-number/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/hidden-symlink/project/.shopify/.gitkeep b/test/fixtures/app-information/hidden-symlink/project/.shopify/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/fixtures/app-information/hidden-symlink/project/.shopify/project.json b/test/fixtures/app-information/hidden-symlink/project/.shopify/project.json new file mode 120000 index 00000000000..bfc7081d371 --- /dev/null +++ b/test/fixtures/app-information/hidden-symlink/project/.shopify/project.json @@ -0,0 +1 @@ +../hidden-target.json \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-symlink/project/hidden-target.json b/test/fixtures/app-information/hidden-symlink/project/hidden-target.json new file mode 100644 index 00000000000..8cd1d9f5f97 --- /dev/null +++ b/test/fixtures/app-information/hidden-symlink/project/hidden-target.json @@ -0,0 +1 @@ +{"dev_store_url":"symlink.myshopify.com"} \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-symlink/project/package.json b/test/fixtures/app-information/hidden-symlink/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/hidden-symlink/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-symlink/project/pnpm-lock.yaml b/test/fixtures/app-information/hidden-symlink/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/hidden-symlink/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/hidden-symlink/project/shopify.app.toml b/test/fixtures/app-information/hidden-symlink/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/hidden-symlink/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/hidden-symlink/project/web/shopify.web.toml b/test/fixtures/app-information/hidden-symlink/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/hidden-symlink/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/hidden-web-discovery/project/.shopify/copied/shopify.web.toml b/test/fixtures/app-information/hidden-web-discovery/project/.shopify/copied/shopify.web.toml new file mode 100644 index 00000000000..c0ee93752d0 --- /dev/null +++ b/test/fixtures/app-information/hidden-web-discovery/project/.shopify/copied/shopify.web.toml @@ -0,0 +1,3 @@ +roles = ["frontend"] +[commands] +dev = "never-run" diff --git a/test/fixtures/app-information/hidden-web-discovery/project/node_modules/dependency/shopify.web.toml b/test/fixtures/app-information/hidden-web-discovery/project/node_modules/dependency/shopify.web.toml new file mode 100644 index 00000000000..3abc6718882 --- /dev/null +++ b/test/fixtures/app-information/hidden-web-discovery/project/node_modules/dependency/shopify.web.toml @@ -0,0 +1 @@ +invalid = [ \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-web-discovery/project/package.json b/test/fixtures/app-information/hidden-web-discovery/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/hidden-web-discovery/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/hidden-web-discovery/project/pnpm-lock.yaml b/test/fixtures/app-information/hidden-web-discovery/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/hidden-web-discovery/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/hidden-web-discovery/project/shopify.app.toml b/test/fixtures/app-information/hidden-web-discovery/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/hidden-web-discovery/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/hidden-web-discovery/project/web/shopify.web.toml b/test/fixtures/app-information/hidden-web-discovery/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/hidden-web-discovery/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/inactive-locales/project/locales/fr.json b/test/fixtures/app-information/inactive-locales/project/locales/fr.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/fixtures/app-information/inactive-locales/project/package.json b/test/fixtures/app-information/inactive-locales/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/inactive-locales/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/inactive-locales/project/pnpm-lock.yaml b/test/fixtures/app-information/inactive-locales/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/inactive-locales/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/inactive-locales/project/shopify.app.toml b/test/fixtures/app-information/inactive-locales/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/inactive-locales/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/inactive-locales/project/web/shopify.web.toml b/test/fixtures/app-information/inactive-locales/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/inactive-locales/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/inactive-web-directories/project/other/shopify.web.toml b/test/fixtures/app-information/inactive-web-directories/project/other/shopify.web.toml new file mode 100644 index 00000000000..cc271196493 --- /dev/null +++ b/test/fixtures/app-information/inactive-web-directories/project/other/shopify.web.toml @@ -0,0 +1,3 @@ +roles = ["background"] +[commands] +dev = "never-run" diff --git a/test/fixtures/app-information/inactive-web-directories/project/package.json b/test/fixtures/app-information/inactive-web-directories/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/inactive-web-directories/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/inactive-web-directories/project/pnpm-lock.yaml b/test/fixtures/app-information/inactive-web-directories/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/inactive-web-directories/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/inactive-web-directories/project/shopify.app.inactive.toml b/test/fixtures/app-information/inactive-web-directories/project/shopify.app.inactive.toml new file mode 100644 index 00000000000..c9a7f1aa8cd --- /dev/null +++ b/test/fixtures/app-information/inactive-web-directories/project/shopify.app.inactive.toml @@ -0,0 +1,11 @@ +web_directories = ["other"] +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/inactive-web-directories/project/shopify.app.toml b/test/fixtures/app-information/inactive-web-directories/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/inactive-web-directories/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/inactive-web-directories/project/web/shopify.web.toml b/test/fixtures/app-information/inactive-web-directories/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/inactive-web-directories/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/invalid-app-client-id-type/project/package.json b/test/fixtures/app-information/invalid-app-client-id-type/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/invalid-app-client-id-type/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/invalid-app-client-id-type/project/pnpm-lock.yaml b/test/fixtures/app-information/invalid-app-client-id-type/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/invalid-app-client-id-type/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/invalid-app-client-id-type/project/shopify.app.toml b/test/fixtures/app-information/invalid-app-client-id-type/project/shopify.app.toml new file mode 100644 index 00000000000..faf411b4c04 --- /dev/null +++ b/test/fixtures/app-information/invalid-app-client-id-type/project/shopify.app.toml @@ -0,0 +1 @@ +client_id = 42 \ No newline at end of file diff --git a/test/fixtures/app-information/invalid-app-client-id-type/project/web/shopify.web.toml b/test/fixtures/app-information/invalid-app-client-id-type/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/invalid-app-client-id-type/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/invalid-app-toml-syntax/project/package.json b/test/fixtures/app-information/invalid-app-toml-syntax/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/invalid-app-toml-syntax/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/invalid-app-toml-syntax/project/pnpm-lock.yaml b/test/fixtures/app-information/invalid-app-toml-syntax/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/invalid-app-toml-syntax/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/invalid-app-toml-syntax/project/shopify.app.toml b/test/fixtures/app-information/invalid-app-toml-syntax/project/shopify.app.toml new file mode 100644 index 00000000000..0e023ed6172 --- /dev/null +++ b/test/fixtures/app-information/invalid-app-toml-syntax/project/shopify.app.toml @@ -0,0 +1 @@ +broken = [ \ No newline at end of file diff --git a/test/fixtures/app-information/invalid-app-toml-syntax/project/web/shopify.web.toml b/test/fixtures/app-information/invalid-app-toml-syntax/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/invalid-app-toml-syntax/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/invalid-configs-help/project/package.json b/test/fixtures/app-information/invalid-configs-help/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/invalid-configs-help/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/invalid-configs-help/project/pnpm-lock.yaml b/test/fixtures/app-information/invalid-configs-help/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/invalid-configs-help/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/invalid-configs-help/project/shopify.app.toml b/test/fixtures/app-information/invalid-configs-help/project/shopify.app.toml new file mode 100644 index 00000000000..c379855a9c6 --- /dev/null +++ b/test/fixtures/app-information/invalid-configs-help/project/shopify.app.toml @@ -0,0 +1 @@ +not = [ \ No newline at end of file diff --git a/test/fixtures/app-information/invalid-configs-help/project/shopify.environments.toml b/test/fixtures/app-information/invalid-configs-help/project/shopify.environments.toml new file mode 100644 index 00000000000..c379855a9c6 --- /dev/null +++ b/test/fixtures/app-information/invalid-configs-help/project/shopify.environments.toml @@ -0,0 +1 @@ +not = [ \ No newline at end of file diff --git a/test/fixtures/app-information/invalid-configs-help/project/web/shopify.web.toml b/test/fixtures/app-information/invalid-configs-help/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/invalid-configs-help/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/legacy-web-type/project/package.json b/test/fixtures/app-information/legacy-web-type/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/legacy-web-type/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/legacy-web-type/project/pnpm-lock.yaml b/test/fixtures/app-information/legacy-web-type/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/legacy-web-type/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/legacy-web-type/project/shopify.app.toml b/test/fixtures/app-information/legacy-web-type/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/legacy-web-type/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/legacy-web-type/project/web/shopify.web.toml b/test/fixtures/app-information/legacy-web-type/project/web/shopify.web.toml new file mode 100644 index 00000000000..1b274d04e84 --- /dev/null +++ b/test/fixtures/app-information/legacy-web-type/project/web/shopify.web.toml @@ -0,0 +1,3 @@ +type = "backend" +[commands] +dev = "must-not-run" diff --git a/test/fixtures/app-information/linked-app-dev-store/project/package.json b/test/fixtures/app-information/linked-app-dev-store/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/linked-app-dev-store/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/linked-app-dev-store/project/pnpm-lock.yaml b/test/fixtures/app-information/linked-app-dev-store/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/linked-app-dev-store/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/linked-app-dev-store/project/shopify.app.toml b/test/fixtures/app-information/linked-app-dev-store/project/shopify.app.toml new file mode 100644 index 00000000000..3cba3c58028 --- /dev/null +++ b/test/fixtures/app-information/linked-app-dev-store/project/shopify.app.toml @@ -0,0 +1,14 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] + +[build] +automatically_update_urls_on_dev = false +dev_store_url = "fixture.myshopify.com" diff --git a/test/fixtures/app-information/linked-app-dev-store/project/web/shopify.web.toml b/test/fixtures/app-information/linked-app-dev-store/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/linked-app-dev-store/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/linked-app/project/package.json b/test/fixtures/app-information/linked-app/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/linked-app/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/linked-app/project/pnpm-lock.yaml b/test/fixtures/app-information/linked-app/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/linked-app/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/linked-app/project/shopify.app.toml b/test/fixtures/app-information/linked-app/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/linked-app/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/linked-app/project/web/shopify.web.toml b/test/fixtures/app-information/linked-app/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/linked-app/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/linked-endpoint-webhooks/project/package.json b/test/fixtures/app-information/linked-endpoint-webhooks/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/linked-endpoint-webhooks/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/linked-endpoint-webhooks/project/pnpm-lock.yaml b/test/fixtures/app-information/linked-endpoint-webhooks/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/linked-endpoint-webhooks/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/linked-endpoint-webhooks/project/shopify.app.toml b/test/fixtures/app-information/linked-endpoint-webhooks/project/shopify.app.toml new file mode 100644 index 00000000000..7492f739f77 --- /dev/null +++ b/test/fixtures/app-information/linked-endpoint-webhooks/project/shopify.app.toml @@ -0,0 +1,18 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] + +[webhooks] +api_version = "2023-10" +endpoint = "/webhooks" +topics = ["orders/create"] +[[webhooks.subscriptions]] +topic = "products/create" +path = "/products" diff --git a/test/fixtures/app-information/linked-endpoint-webhooks/project/web/shopify.web.toml b/test/fixtures/app-information/linked-endpoint-webhooks/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/linked-endpoint-webhooks/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/linked-legacy-privacy/project/package.json b/test/fixtures/app-information/linked-legacy-privacy/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/linked-legacy-privacy/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/linked-legacy-privacy/project/pnpm-lock.yaml b/test/fixtures/app-information/linked-legacy-privacy/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/linked-legacy-privacy/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/linked-legacy-privacy/project/shopify.app.toml b/test/fixtures/app-information/linked-legacy-privacy/project/shopify.app.toml new file mode 100644 index 00000000000..ad4382541a1 --- /dev/null +++ b/test/fixtures/app-information/linked-legacy-privacy/project/shopify.app.toml @@ -0,0 +1,17 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] + +[webhooks] +api_version = "2023-07" +[webhooks.privacy_compliance] +customer_deletion_url = "/privacy/delete" +customer_data_request_url = "/privacy/data" +shop_deletion_url = "/privacy/shop" diff --git a/test/fixtures/app-information/linked-legacy-privacy/project/web/shopify.web.toml b/test/fixtures/app-information/linked-legacy-privacy/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/linked-legacy-privacy/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/linked-mixed-privacy/project/package.json b/test/fixtures/app-information/linked-mixed-privacy/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/linked-mixed-privacy/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/linked-mixed-privacy/project/pnpm-lock.yaml b/test/fixtures/app-information/linked-mixed-privacy/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/linked-mixed-privacy/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/linked-mixed-privacy/project/shopify.app.toml b/test/fixtures/app-information/linked-mixed-privacy/project/shopify.app.toml new file mode 100644 index 00000000000..305f86f67fd --- /dev/null +++ b/test/fixtures/app-information/linked-mixed-privacy/project/shopify.app.toml @@ -0,0 +1,18 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] + +[webhooks] +api_version = "2025-10" +[webhooks.privacy_compliance] +customer_deletion_url = "/old" +[[webhooks.subscriptions]] +compliance_topics = ["customers/redact"] +uri = "/new" diff --git a/test/fixtures/app-information/linked-mixed-privacy/project/web/shopify.web.toml b/test/fixtures/app-information/linked-mixed-privacy/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/linked-mixed-privacy/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/linked-old-subscription/project/package.json b/test/fixtures/app-information/linked-old-subscription/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/linked-old-subscription/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/linked-old-subscription/project/pnpm-lock.yaml b/test/fixtures/app-information/linked-old-subscription/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/linked-old-subscription/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/linked-old-subscription/project/shopify.app.toml b/test/fixtures/app-information/linked-old-subscription/project/shopify.app.toml new file mode 100644 index 00000000000..f4e910db53d --- /dev/null +++ b/test/fixtures/app-information/linked-old-subscription/project/shopify.app.toml @@ -0,0 +1,18 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] + +[webhooks] +api_version = "2024-01" +[[webhooks.subscriptions]] +topics = ["orders/create"] +uri = "/hooks" +sub_topic = "old-subtopic" +metafield_namespaces = ["fixture"] diff --git a/test/fixtures/app-information/linked-old-subscription/project/web/shopify.web.toml b/test/fixtures/app-information/linked-old-subscription/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/linked-old-subscription/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/linked-required-scopes/project/package.json b/test/fixtures/app-information/linked-required-scopes/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/linked-required-scopes/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/linked-required-scopes/project/pnpm-lock.yaml b/test/fixtures/app-information/linked-required-scopes/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/linked-required-scopes/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/linked-required-scopes/project/shopify.app.toml b/test/fixtures/app-information/linked-required-scopes/project/shopify.app.toml new file mode 100644 index 00000000000..5acd126643e --- /dev/null +++ b/test/fixtures/app-information/linked-required-scopes/project/shopify.app.toml @@ -0,0 +1,19 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +required_scopes = ["read_products", "write_products"] +optional_scopes = ["read_orders"] + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] + +[build] +include_config_on_deploy = true +[webhooks] +api_version = "2025-10" +[[webhooks.subscriptions]] +compliance_topics = ["customers/redact", "customers/data_request", "shop/redact"] +uri = "/privacy" diff --git a/test/fixtures/app-information/linked-required-scopes/project/web/shopify.web.toml b/test/fixtures/app-information/linked-required-scopes/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/linked-required-scopes/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/linked-uri-shorthand/project/package.json b/test/fixtures/app-information/linked-uri-shorthand/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/linked-uri-shorthand/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/linked-uri-shorthand/project/pnpm-lock.yaml b/test/fixtures/app-information/linked-uri-shorthand/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/linked-uri-shorthand/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/linked-uri-shorthand/project/shopify.app.toml b/test/fixtures/app-information/linked-uri-shorthand/project/shopify.app.toml new file mode 100644 index 00000000000..87abfa1ccbc --- /dev/null +++ b/test/fixtures/app-information/linked-uri-shorthand/project/shopify.app.toml @@ -0,0 +1,15 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] + +[webhooks] +api_version = "2023-10" +uri = "/webhooks" +topics = ["orders/create"] diff --git a/test/fixtures/app-information/linked-uri-shorthand/project/web/shopify.web.toml b/test/fixtures/app-information/linked-uri-shorthand/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/linked-uri-shorthand/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/local-cli-dependency/project/package.json b/test/fixtures/app-information/local-cli-dependency/project/package.json new file mode 100644 index 00000000000..d8d57014c96 --- /dev/null +++ b/test/fixtures/app-information/local-cli-dependency/project/package.json @@ -0,0 +1 @@ +{"dependencies":{"@shopify/cli":"4.0.0"}} \ No newline at end of file diff --git a/test/fixtures/app-information/local-cli-dependency/project/pnpm-lock.yaml b/test/fixtures/app-information/local-cli-dependency/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/local-cli-dependency/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/local-cli-dependency/project/shopify.app.toml b/test/fixtures/app-information/local-cli-dependency/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/local-cli-dependency/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/local-cli-dependency/project/web/shopify.web.toml b/test/fixtures/app-information/local-cli-dependency/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/local-cli-dependency/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/locales-empty/project/locales/en.default.json b/test/fixtures/app-information/locales-empty/project/locales/en.default.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/fixtures/app-information/locales-empty/project/package.json b/test/fixtures/app-information/locales-empty/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/locales-empty/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/locales-empty/project/pnpm-lock.yaml b/test/fixtures/app-information/locales-empty/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/locales-empty/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/locales-empty/project/shopify.app.toml b/test/fixtures/app-information/locales-empty/project/shopify.app.toml new file mode 100644 index 00000000000..bff250165b8 --- /dev/null +++ b/test/fixtures/app-information/locales-empty/project/shopify.app.toml @@ -0,0 +1,13 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] + +[fixture_configuration] +enabled = true diff --git a/test/fixtures/app-information/locales-empty/project/web/shopify.web.toml b/test/fixtures/app-information/locales-empty/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/locales-empty/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/locales-invalid-utf8/project/locales/en.default.json b/test/fixtures/app-information/locales-invalid-utf8/project/locales/en.default.json new file mode 100644 index 00000000000..46b134b197f --- /dev/null +++ b/test/fixtures/app-information/locales-invalid-utf8/project/locales/en.default.json @@ -0,0 +1 @@ +ÿþ \ No newline at end of file diff --git a/test/fixtures/app-information/locales-invalid-utf8/project/package.json b/test/fixtures/app-information/locales-invalid-utf8/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/locales-invalid-utf8/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/locales-invalid-utf8/project/pnpm-lock.yaml b/test/fixtures/app-information/locales-invalid-utf8/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/locales-invalid-utf8/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/locales-invalid-utf8/project/shopify.app.toml b/test/fixtures/app-information/locales-invalid-utf8/project/shopify.app.toml new file mode 100644 index 00000000000..bff250165b8 --- /dev/null +++ b/test/fixtures/app-information/locales-invalid-utf8/project/shopify.app.toml @@ -0,0 +1,13 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] + +[fixture_configuration] +enabled = true diff --git a/test/fixtures/app-information/locales-invalid-utf8/project/web/shopify.web.toml b/test/fixtures/app-information/locales-invalid-utf8/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/locales-invalid-utf8/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/locales-multiple-defaults/project/locales/en.default.json b/test/fixtures/app-information/locales-multiple-defaults/project/locales/en.default.json new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/test/fixtures/app-information/locales-multiple-defaults/project/locales/en.default.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/fixtures/app-information/locales-multiple-defaults/project/locales/fr.default.json b/test/fixtures/app-information/locales-multiple-defaults/project/locales/fr.default.json new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/test/fixtures/app-information/locales-multiple-defaults/project/locales/fr.default.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/fixtures/app-information/locales-multiple-defaults/project/package.json b/test/fixtures/app-information/locales-multiple-defaults/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/locales-multiple-defaults/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/locales-multiple-defaults/project/pnpm-lock.yaml b/test/fixtures/app-information/locales-multiple-defaults/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/locales-multiple-defaults/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/locales-multiple-defaults/project/shopify.app.toml b/test/fixtures/app-information/locales-multiple-defaults/project/shopify.app.toml new file mode 100644 index 00000000000..bff250165b8 --- /dev/null +++ b/test/fixtures/app-information/locales-multiple-defaults/project/shopify.app.toml @@ -0,0 +1,13 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] + +[fixture_configuration] +enabled = true diff --git a/test/fixtures/app-information/locales-multiple-defaults/project/web/shopify.web.toml b/test/fixtures/app-information/locales-multiple-defaults/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/locales-multiple-defaults/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/locales-no-default/project/locales/fr.json b/test/fixtures/app-information/locales-no-default/project/locales/fr.json new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/test/fixtures/app-information/locales-no-default/project/locales/fr.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/fixtures/app-information/locales-no-default/project/package.json b/test/fixtures/app-information/locales-no-default/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/locales-no-default/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/locales-no-default/project/pnpm-lock.yaml b/test/fixtures/app-information/locales-no-default/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/locales-no-default/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/locales-no-default/project/shopify.app.toml b/test/fixtures/app-information/locales-no-default/project/shopify.app.toml new file mode 100644 index 00000000000..bff250165b8 --- /dev/null +++ b/test/fixtures/app-information/locales-no-default/project/shopify.app.toml @@ -0,0 +1,13 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] + +[fixture_configuration] +enabled = true diff --git a/test/fixtures/app-information/locales-no-default/project/web/shopify.web.toml b/test/fixtures/app-information/locales-no-default/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/locales-no-default/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/locales-not-json/project/locales/en.default.json b/test/fixtures/app-information/locales-not-json/project/locales/en.default.json new file mode 100644 index 00000000000..69193e9994a --- /dev/null +++ b/test/fixtures/app-information/locales-not-json/project/locales/en.default.json @@ -0,0 +1 @@ +not JSON but valid UTF-8 \ No newline at end of file diff --git a/test/fixtures/app-information/locales-not-json/project/package.json b/test/fixtures/app-information/locales-not-json/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/locales-not-json/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/locales-not-json/project/pnpm-lock.yaml b/test/fixtures/app-information/locales-not-json/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/locales-not-json/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/locales-not-json/project/shopify.app.toml b/test/fixtures/app-information/locales-not-json/project/shopify.app.toml new file mode 100644 index 00000000000..bff250165b8 --- /dev/null +++ b/test/fixtures/app-information/locales-not-json/project/shopify.app.toml @@ -0,0 +1,13 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] + +[fixture_configuration] +enabled = true diff --git a/test/fixtures/app-information/locales-not-json/project/web/shopify.web.toml b/test/fixtures/app-information/locales-not-json/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/locales-not-json/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/locales-valid/project/locales/en.default.json b/test/fixtures/app-information/locales-valid/project/locales/en.default.json new file mode 100644 index 00000000000..661dd424680 --- /dev/null +++ b/test/fixtures/app-information/locales-valid/project/locales/en.default.json @@ -0,0 +1 @@ +{"title":"Fixture"} \ No newline at end of file diff --git a/test/fixtures/app-information/locales-valid/project/package.json b/test/fixtures/app-information/locales-valid/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/locales-valid/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/locales-valid/project/pnpm-lock.yaml b/test/fixtures/app-information/locales-valid/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/locales-valid/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/locales-valid/project/shopify.app.toml b/test/fixtures/app-information/locales-valid/project/shopify.app.toml new file mode 100644 index 00000000000..bff250165b8 --- /dev/null +++ b/test/fixtures/app-information/locales-valid/project/shopify.app.toml @@ -0,0 +1,13 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] + +[fixture_configuration] +enabled = true diff --git a/test/fixtures/app-information/locales-valid/project/web/shopify.web.toml b/test/fixtures/app-information/locales-valid/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/locales-valid/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/localized-module-wrong-locale-location/project/extensions/not-discovered/locales/fr.json b/test/fixtures/app-information/localized-module-wrong-locale-location/project/extensions/not-discovered/locales/fr.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/fixtures/app-information/localized-module-wrong-locale-location/project/package.json b/test/fixtures/app-information/localized-module-wrong-locale-location/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/localized-module-wrong-locale-location/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/localized-module-wrong-locale-location/project/pnpm-lock.yaml b/test/fixtures/app-information/localized-module-wrong-locale-location/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/localized-module-wrong-locale-location/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/localized-module-wrong-locale-location/project/shopify.app.toml b/test/fixtures/app-information/localized-module-wrong-locale-location/project/shopify.app.toml new file mode 100644 index 00000000000..bff250165b8 --- /dev/null +++ b/test/fixtures/app-information/localized-module-wrong-locale-location/project/shopify.app.toml @@ -0,0 +1,13 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] + +[fixture_configuration] +enabled = true diff --git a/test/fixtures/app-information/localized-module-wrong-locale-location/project/web/shopify.web.toml b/test/fixtures/app-information/localized-module-wrong-locale-location/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/localized-module-wrong-locale-location/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/localized-module/project/package.json b/test/fixtures/app-information/localized-module/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/localized-module/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/localized-module/project/pnpm-lock.yaml b/test/fixtures/app-information/localized-module/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/localized-module/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/localized-module/project/shopify.app.toml b/test/fixtures/app-information/localized-module/project/shopify.app.toml new file mode 100644 index 00000000000..bff250165b8 --- /dev/null +++ b/test/fixtures/app-information/localized-module/project/shopify.app.toml @@ -0,0 +1,13 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] + +[fixture_configuration] +enabled = true diff --git a/test/fixtures/app-information/localized-module/project/web/shopify.web.toml b/test/fixtures/app-information/localized-module/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/localized-module/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/manifest-bom/project/package.json b/test/fixtures/app-information/manifest-bom/project/package.json new file mode 100644 index 00000000000..22fdca1b268 --- /dev/null +++ b/test/fixtures/app-information/manifest-bom/project/package.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/fixtures/app-information/manifest-bom/project/pnpm-lock.yaml b/test/fixtures/app-information/manifest-bom/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/manifest-bom/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/manifest-bom/project/shopify.app.toml b/test/fixtures/app-information/manifest-bom/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/manifest-bom/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/manifest-bom/project/web/shopify.web.toml b/test/fixtures/app-information/manifest-bom/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/manifest-bom/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/manifest-dependencies-workspaces/project/package.json b/test/fixtures/app-information/manifest-dependencies-workspaces/project/package.json new file mode 100644 index 00000000000..47b109d39f8 --- /dev/null +++ b/test/fixtures/app-information/manifest-dependencies-workspaces/project/package.json @@ -0,0 +1 @@ +{"dependencies":{"example":"1"},"devDependencies":{"example":"2"},"peerDependencies":{"peer":"3"},"workspaces":[]} \ No newline at end of file diff --git a/test/fixtures/app-information/manifest-dependencies-workspaces/project/pnpm-lock.yaml b/test/fixtures/app-information/manifest-dependencies-workspaces/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/manifest-dependencies-workspaces/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/manifest-dependencies-workspaces/project/shopify.app.toml b/test/fixtures/app-information/manifest-dependencies-workspaces/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/manifest-dependencies-workspaces/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/manifest-dependencies-workspaces/project/web/shopify.web.toml b/test/fixtures/app-information/manifest-dependencies-workspaces/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/manifest-dependencies-workspaces/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/manifest-invalid-json/project/package.json b/test/fixtures/app-information/manifest-invalid-json/project/package.json new file mode 100644 index 00000000000..d3db885cc09 --- /dev/null +++ b/test/fixtures/app-information/manifest-invalid-json/project/package.json @@ -0,0 +1 @@ +{bad \ No newline at end of file diff --git a/test/fixtures/app-information/manifest-invalid-json/project/pnpm-lock.yaml b/test/fixtures/app-information/manifest-invalid-json/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/manifest-invalid-json/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/manifest-invalid-json/project/shopify.app.toml b/test/fixtures/app-information/manifest-invalid-json/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/manifest-invalid-json/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/manifest-invalid-json/project/web/shopify.web.toml b/test/fixtures/app-information/manifest-invalid-json/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/manifest-invalid-json/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/manifest-null/project/package.json b/test/fixtures/app-information/manifest-null/project/package.json new file mode 100644 index 00000000000..ec747fa47dd --- /dev/null +++ b/test/fixtures/app-information/manifest-null/project/package.json @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/test/fixtures/app-information/manifest-null/project/pnpm-lock.yaml b/test/fixtures/app-information/manifest-null/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/manifest-null/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/manifest-null/project/shopify.app.toml b/test/fixtures/app-information/manifest-null/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/manifest-null/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/manifest-null/project/web/shopify.web.toml b/test/fixtures/app-information/manifest-null/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/manifest-null/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/missing-manifest/project/pnpm-lock.yaml b/test/fixtures/app-information/missing-manifest/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/missing-manifest/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/missing-manifest/project/shopify.app.toml b/test/fixtures/app-information/missing-manifest/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/missing-manifest/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/missing-manifest/project/web/shopify.web.toml b/test/fixtures/app-information/missing-manifest/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/missing-manifest/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/named-config-dotenv/project/.env b/test/fixtures/app-information/named-config-dotenv/project/.env new file mode 100644 index 00000000000..ffa05834219 --- /dev/null +++ b/test/fixtures/app-information/named-config-dotenv/project/.env @@ -0,0 +1,2 @@ +DEFAULT_ONLY=no +SHARED=default diff --git a/test/fixtures/app-information/named-config-dotenv/project/.env.staging b/test/fixtures/app-information/named-config-dotenv/project/.env.staging new file mode 100644 index 00000000000..672bb8159ba --- /dev/null +++ b/test/fixtures/app-information/named-config-dotenv/project/.env.staging @@ -0,0 +1 @@ +SHARED=staging diff --git a/test/fixtures/app-information/named-config-dotenv/project/package.json b/test/fixtures/app-information/named-config-dotenv/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/named-config-dotenv/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/named-config-dotenv/project/pnpm-lock.yaml b/test/fixtures/app-information/named-config-dotenv/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/named-config-dotenv/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/named-config-dotenv/project/shopify.app.staging.toml b/test/fixtures/app-information/named-config-dotenv/project/shopify.app.staging.toml new file mode 100644 index 00000000000..c87e2d4a39d --- /dev/null +++ b/test/fixtures/app-information/named-config-dotenv/project/shopify.app.staging.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Staging fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/named-config-dotenv/project/shopify.app.toml b/test/fixtures/app-information/named-config-dotenv/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/named-config-dotenv/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/named-config-dotenv/project/web/shopify.web.toml b/test/fixtures/app-information/named-config-dotenv/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/named-config-dotenv/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/named-config-no-dotenv/project/.env b/test/fixtures/app-information/named-config-no-dotenv/project/.env new file mode 100644 index 00000000000..94d2d1f3262 --- /dev/null +++ b/test/fixtures/app-information/named-config-no-dotenv/project/.env @@ -0,0 +1 @@ +NOT_SELECTED=value diff --git a/test/fixtures/app-information/named-config-no-dotenv/project/package.json b/test/fixtures/app-information/named-config-no-dotenv/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/named-config-no-dotenv/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/named-config-no-dotenv/project/pnpm-lock.yaml b/test/fixtures/app-information/named-config-no-dotenv/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/named-config-no-dotenv/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/named-config-no-dotenv/project/shopify.app.staging.toml b/test/fixtures/app-information/named-config-no-dotenv/project/shopify.app.staging.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/named-config-no-dotenv/project/shopify.app.staging.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/named-config-no-dotenv/project/shopify.app.toml b/test/fixtures/app-information/named-config-no-dotenv/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/named-config-no-dotenv/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/named-config-no-dotenv/project/web/shopify.web.toml b/test/fixtures/app-information/named-config-no-dotenv/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/named-config-no-dotenv/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/named-config-only/project/package.json b/test/fixtures/app-information/named-config-only/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/named-config-only/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/named-config-only/project/pnpm-lock.yaml b/test/fixtures/app-information/named-config-only/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/named-config-only/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/named-config-only/project/shopify.app.staging.toml b/test/fixtures/app-information/named-config-only/project/shopify.app.staging.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/named-config-only/project/shopify.app.staging.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/named-config-only/project/web/shopify.web.toml b/test/fixtures/app-information/named-config-only/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/named-config-only/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/named-staging-fixture/project/package.json b/test/fixtures/app-information/named-staging-fixture/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/named-staging-fixture/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/named-staging-fixture/project/pnpm-lock.yaml b/test/fixtures/app-information/named-staging-fixture/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/named-staging-fixture/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/named-staging-fixture/project/shopify.app.staging.toml b/test/fixtures/app-information/named-staging-fixture/project/shopify.app.staging.toml new file mode 100644 index 00000000000..c87e2d4a39d --- /dev/null +++ b/test/fixtures/app-information/named-staging-fixture/project/shopify.app.staging.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Staging fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/named-staging-fixture/project/shopify.app.toml b/test/fixtures/app-information/named-staging-fixture/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/named-staging-fixture/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/named-staging-fixture/project/web/shopify.web.toml b/test/fixtures/app-information/named-staging-fixture/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/named-staging-fixture/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/named-staging/project/package.json b/test/fixtures/app-information/named-staging/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/named-staging/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/named-staging/project/pnpm-lock.yaml b/test/fixtures/app-information/named-staging/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/named-staging/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/named-staging/project/shopify.app.staging.toml b/test/fixtures/app-information/named-staging/project/shopify.app.staging.toml new file mode 100644 index 00000000000..edd3881c130 --- /dev/null +++ b/test/fixtures/app-information/named-staging/project/shopify.app.staging.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Staging" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/named-staging/project/shopify.app.toml b/test/fixtures/app-information/named-staging/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/named-staging/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/named-staging/project/web/shopify.web.toml b/test/fixtures/app-information/named-staging/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/named-staging/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/no-package-manager-marker/project/package.json b/test/fixtures/app-information/no-package-manager-marker/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/no-package-manager-marker/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/no-package-manager-marker/project/shopify.app.toml b/test/fixtures/app-information/no-package-manager-marker/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/no-package-manager-marker/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/no-package-manager-marker/project/web/shopify.web.toml b/test/fixtures/app-information/no-package-manager-marker/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/no-package-manager-marker/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/no-project/project/.gitkeep b/test/fixtures/app-information/no-project/project/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/fixtures/app-information/no-web/project/package.json b/test/fixtures/app-information/no-web/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/no-web/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/no-web/project/pnpm-lock.yaml b/test/fixtures/app-information/no-web/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/no-web/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/no-web/project/shopify.app.toml b/test/fixtures/app-information/no-web/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/no-web/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/package-manager-bun/project/bun.lock b/test/fixtures/app-information/package-manager-bun/project/bun.lock new file mode 100644 index 00000000000..efc7cb2ff22 --- /dev/null +++ b/test/fixtures/app-information/package-manager-bun/project/bun.lock @@ -0,0 +1 @@ +not parsed by detection \ No newline at end of file diff --git a/test/fixtures/app-information/package-manager-bun/project/package.json b/test/fixtures/app-information/package-manager-bun/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/package-manager-bun/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/package-manager-bun/project/shopify.app.toml b/test/fixtures/app-information/package-manager-bun/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/package-manager-bun/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/package-manager-bun/project/web/shopify.web.toml b/test/fixtures/app-information/package-manager-bun/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/package-manager-bun/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/package-manager-npm/project/package-lock.json b/test/fixtures/app-information/package-manager-npm/project/package-lock.json new file mode 100644 index 00000000000..efc7cb2ff22 --- /dev/null +++ b/test/fixtures/app-information/package-manager-npm/project/package-lock.json @@ -0,0 +1 @@ +not parsed by detection \ No newline at end of file diff --git a/test/fixtures/app-information/package-manager-npm/project/package.json b/test/fixtures/app-information/package-manager-npm/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/package-manager-npm/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/package-manager-npm/project/shopify.app.toml b/test/fixtures/app-information/package-manager-npm/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/package-manager-npm/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/package-manager-npm/project/web/shopify.web.toml b/test/fixtures/app-information/package-manager-npm/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/package-manager-npm/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/package-manager-pnpm/project/package.json b/test/fixtures/app-information/package-manager-pnpm/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/package-manager-pnpm/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/package-manager-pnpm/project/pnpm-lock.yaml b/test/fixtures/app-information/package-manager-pnpm/project/pnpm-lock.yaml new file mode 100644 index 00000000000..efc7cb2ff22 --- /dev/null +++ b/test/fixtures/app-information/package-manager-pnpm/project/pnpm-lock.yaml @@ -0,0 +1 @@ +not parsed by detection \ No newline at end of file diff --git a/test/fixtures/app-information/package-manager-pnpm/project/shopify.app.toml b/test/fixtures/app-information/package-manager-pnpm/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/package-manager-pnpm/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/package-manager-pnpm/project/web/shopify.web.toml b/test/fixtures/app-information/package-manager-pnpm/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/package-manager-pnpm/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/package-manager-yarn/project/package.json b/test/fixtures/app-information/package-manager-yarn/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/package-manager-yarn/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/package-manager-yarn/project/shopify.app.toml b/test/fixtures/app-information/package-manager-yarn/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/package-manager-yarn/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/package-manager-yarn/project/web/shopify.web.toml b/test/fixtures/app-information/package-manager-yarn/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/package-manager-yarn/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/package-manager-yarn/project/yarn.lock b/test/fixtures/app-information/package-manager-yarn/project/yarn.lock new file mode 100644 index 00000000000..efc7cb2ff22 --- /dev/null +++ b/test/fixtures/app-information/package-manager-yarn/project/yarn.lock @@ -0,0 +1 @@ +not parsed by detection \ No newline at end of file diff --git a/test/fixtures/app-information/package-marker-ancestor/project/package-lock.json b/test/fixtures/app-information/package-marker-ancestor/project/package-lock.json new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/test/fixtures/app-information/package-marker-ancestor/project/package-lock.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/fixtures/app-information/package-marker-ancestor/project/package.json b/test/fixtures/app-information/package-marker-ancestor/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/package-marker-ancestor/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/package-marker-ancestor/project/shopify.app.toml b/test/fixtures/app-information/package-marker-ancestor/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/package-marker-ancestor/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/package-marker-ancestor/project/web/shopify.web.toml b/test/fixtures/app-information/package-marker-ancestor/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/package-marker-ancestor/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/package-marker-ancestor/yarn.lock b/test/fixtures/app-information/package-marker-ancestor/yarn.lock new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/fixtures/app-information/report-components/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/report-components/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/report-components/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/report-components/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/report-components/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/report-components/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/report-components/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/report-components/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..52a431737b3 --- /dev/null +++ b/test/fixtures/app-information/report-components/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,15 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.metafields]] +namespace = "fixture" +key = "first" +[[extensions.metafields]] +namespace = "fixture" +key = "second" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" diff --git a/test/fixtures/app-information/report-components/project/extensions/ui/src/index.ts b/test/fixtures/app-information/report-components/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/report-components/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/report-components/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/report-components/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/report-components/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/report-components/project/package.json b/test/fixtures/app-information/report-components/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/report-components/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/report-components/project/pnpm-lock.yaml b/test/fixtures/app-information/report-components/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/report-components/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/report-components/project/shopify.app.toml b/test/fixtures/app-information/report-components/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/report-components/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/report-components/project/web/shopify.web.toml b/test/fixtures/app-information/report-components/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/report-components/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/template-current/project/package.json b/test/fixtures/app-information/template-current/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/template-current/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/template-current/project/pnpm-lock.yaml b/test/fixtures/app-information/template-current/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/template-current/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/template-current/project/shopify.app.toml b/test/fixtures/app-information/template-current/project/shopify.app.toml new file mode 100644 index 00000000000..2a340caac36 --- /dev/null +++ b/test/fixtures/app-information/template-current/project/shopify.app.toml @@ -0,0 +1,3 @@ +client_id = "" +[access_scopes] +scopes = "read_products" diff --git a/test/fixtures/app-information/template-current/project/web/shopify.web.toml b/test/fixtures/app-information/template-current/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/template-current/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/template-hybrid-client-id/project/package.json b/test/fixtures/app-information/template-hybrid-client-id/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/template-hybrid-client-id/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/template-hybrid-client-id/project/pnpm-lock.yaml b/test/fixtures/app-information/template-hybrid-client-id/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/template-hybrid-client-id/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/template-hybrid-client-id/project/shopify.app.toml b/test/fixtures/app-information/template-hybrid-client-id/project/shopify.app.toml new file mode 100644 index 00000000000..834310b7a26 --- /dev/null +++ b/test/fixtures/app-information/template-hybrid-client-id/project/shopify.app.toml @@ -0,0 +1,2 @@ +client_id = "" +scopes = "read_products" diff --git a/test/fixtures/app-information/template-hybrid-client-id/project/web/shopify.web.toml b/test/fixtures/app-information/template-hybrid-client-id/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/template-hybrid-client-id/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/template-name-only/project/package.json b/test/fixtures/app-information/template-name-only/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/template-name-only/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/template-name-only/project/pnpm-lock.yaml b/test/fixtures/app-information/template-name-only/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/template-name-only/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/template-name-only/project/shopify.app.toml b/test/fixtures/app-information/template-name-only/project/shopify.app.toml new file mode 100644 index 00000000000..57fd6ef2627 --- /dev/null +++ b/test/fixtures/app-information/template-name-only/project/shopify.app.toml @@ -0,0 +1 @@ +name = "Legacy seed" diff --git a/test/fixtures/app-information/template-name-only/project/web/shopify.web.toml b/test/fixtures/app-information/template-name-only/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/template-name-only/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/template-root-scopes/project/package.json b/test/fixtures/app-information/template-root-scopes/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/template-root-scopes/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/template-root-scopes/project/pnpm-lock.yaml b/test/fixtures/app-information/template-root-scopes/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/template-root-scopes/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/template-root-scopes/project/shopify.app.toml b/test/fixtures/app-information/template-root-scopes/project/shopify.app.toml new file mode 100644 index 00000000000..2e7b5e2aaa3 --- /dev/null +++ b/test/fixtures/app-information/template-root-scopes/project/shopify.app.toml @@ -0,0 +1 @@ +scopes = "read_products" diff --git a/test/fixtures/app-information/template-root-scopes/project/web/shopify.web.toml b/test/fixtures/app-information/template-root-scopes/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/template-root-scopes/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/theme-extension/project/extensions/fixture/locales/fr.json b/test/fixtures/app-information/theme-extension/project/extensions/fixture/locales/fr.json new file mode 100644 index 00000000000..4f80ad883c3 --- /dev/null +++ b/test/fixtures/app-information/theme-extension/project/extensions/fixture/locales/fr.json @@ -0,0 +1 @@ +invalid bytes for deploy \ No newline at end of file diff --git a/test/fixtures/app-information/theme-extension/project/extensions/fixture/shopify.extension.toml b/test/fixtures/app-information/theme-extension/project/extensions/fixture/shopify.extension.toml new file mode 100644 index 00000000000..dfa8cad78b7 --- /dev/null +++ b/test/fixtures/app-information/theme-extension/project/extensions/fixture/shopify.extension.toml @@ -0,0 +1,5 @@ +[[extensions]] +name = "Fixture" +handle = "fixture" +type = "theme" +uid = "fixture-uid" diff --git a/test/fixtures/app-information/theme-extension/project/package.json b/test/fixtures/app-information/theme-extension/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/theme-extension/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/theme-extension/project/pnpm-lock.yaml b/test/fixtures/app-information/theme-extension/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/theme-extension/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/theme-extension/project/shopify.app.toml b/test/fixtures/app-information/theme-extension/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/theme-extension/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/theme-extension/project/web/shopify.web.toml b/test/fixtures/app-information/theme-extension/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/theme-extension/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-ancestor-tsconfig/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-ancestor-tsconfig/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..4e3fe03106e --- /dev/null +++ b/test/fixtures/app-information/ui-ancestor-tsconfig/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,9 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" diff --git a/test/fixtures/app-information/ui-ancestor-tsconfig/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-ancestor-tsconfig/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-ancestor-tsconfig/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-ancestor-tsconfig/project/package.json b/test/fixtures/app-information/ui-ancestor-tsconfig/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-ancestor-tsconfig/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-ancestor-tsconfig/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-ancestor-tsconfig/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-ancestor-tsconfig/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-ancestor-tsconfig/project/shopify.app.toml b/test/fixtures/app-information/ui-ancestor-tsconfig/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-ancestor-tsconfig/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-ancestor-tsconfig/project/tsconfig.json b/test/fixtures/app-information/ui-ancestor-tsconfig/project/tsconfig.json new file mode 100644 index 00000000000..74392a57edb --- /dev/null +++ b/test/fixtures/app-information/ui-ancestor-tsconfig/project/tsconfig.json @@ -0,0 +1 @@ +{"include":["extensions/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-ancestor-tsconfig/project/web/shopify.web.toml b/test/fixtures/app-information/ui-ancestor-tsconfig/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-ancestor-tsconfig/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/intent.json b/test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/intent.json new file mode 100644 index 00000000000..4e83352a06d --- /dev/null +++ b/test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/intent.json @@ -0,0 +1 @@ +{"inputSchema":{"type":"object"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..826ad70efce --- /dev/null +++ b/test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,17 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" +[[extensions.targeting.intents]] +action = "edit" +type = "fixture" +schema = "./intent.json" +[[extensions.targeting.intents]] +action = "edit" +type = "fixture" +schema = "./intent.json" diff --git a/test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-duplicate-intents/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-duplicate-intents/project/package.json b/test/fixtures/app-information/ui-duplicate-intents/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-duplicate-intents/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-duplicate-intents/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-duplicate-intents/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-duplicate-intents/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-duplicate-intents/project/shopify.app.toml b/test/fixtures/app-information/ui-duplicate-intents/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-duplicate-intents/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-duplicate-intents/project/web/shopify.web.toml b/test/fixtures/app-information/ui-duplicate-intents/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-duplicate-intents/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..a2ed81e34e9 --- /dev/null +++ b/test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,8 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" diff --git a/test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-errors-no-uid/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-errors-no-uid/project/package.json b/test/fixtures/app-information/ui-errors-no-uid/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-errors-no-uid/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-errors-no-uid/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-errors-no-uid/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-errors-no-uid/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-errors-no-uid/project/shopify.app.toml b/test/fixtures/app-information/ui-errors-no-uid/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-errors-no-uid/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-errors-no-uid/project/web/shopify.web.toml b/test/fixtures/app-information/ui-errors-no-uid/project/web/shopify.web.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/fixtures/app-information/ui-extension/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-extension/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-extension/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-extension/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-extension/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-extension/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-extension/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-extension/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..4e3fe03106e --- /dev/null +++ b/test/fixtures/app-information/ui-extension/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,9 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" diff --git a/test/fixtures/app-information/ui-extension/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-extension/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-extension/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-extension/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-extension/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-extension/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-extension/project/package.json b/test/fixtures/app-information/ui-extension/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-extension/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-extension/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-extension/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-extension/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-extension/project/shopify.app.toml b/test/fixtures/app-information/ui-extension/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-extension/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-extension/project/web/shopify.web.toml b/test/fixtures/app-information/ui-extension/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-extension/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..5b8a35b54c8 --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,10 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" +tools = "./tools.json" diff --git a/test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/tools.json b/test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/tools.json new file mode 100644 index 00000000000..8dc690c5f23 --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/tools.json @@ -0,0 +1 @@ +[{"name":"lookup","description":"","inputSchema":{"$ref":"https://schemas.example.test/missing.json"}}] \ No newline at end of file diff --git a/test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-failure/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-http-schema-failure/project/package.json b/test/fixtures/app-information/ui-http-schema-failure/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-failure/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-http-schema-failure/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-http-schema-failure/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-failure/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-http-schema-failure/project/shopify.app.toml b/test/fixtures/app-information/ui-http-schema-failure/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-failure/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-http-schema-failure/project/web/shopify.web.toml b/test/fixtures/app-information/ui-http-schema-failure/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-failure/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..5b8a35b54c8 --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,10 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" +tools = "./tools.json" diff --git a/test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/tools.json b/test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/tools.json new file mode 100644 index 00000000000..7b6d65ad26b --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/tools.json @@ -0,0 +1 @@ +[{"name":"lookup","description":"","inputSchema":{"$ref":"https://schemas.example.test/input.json"}}] \ No newline at end of file diff --git a/test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-reference/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-http-schema-reference/project/package.json b/test/fixtures/app-information/ui-http-schema-reference/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-reference/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-http-schema-reference/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-http-schema-reference/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-reference/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-http-schema-reference/project/shopify.app.toml b/test/fixtures/app-information/ui-http-schema-reference/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-reference/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-http-schema-reference/project/web/shopify.web.toml b/test/fixtures/app-information/ui-http-schema-reference/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-http-schema-reference/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-imported-source/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-imported-source/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-imported-source/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-imported-source/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-imported-source/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-imported-source/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-imported-source/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-imported-source/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..4e3fe03106e --- /dev/null +++ b/test/fixtures/app-information/ui-imported-source/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,9 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" diff --git a/test/fixtures/app-information/ui-imported-source/project/extensions/ui/src/helper.ts b/test/fixtures/app-information/ui-imported-source/project/extensions/ui/src/helper.ts new file mode 100644 index 00000000000..3a8684ed358 --- /dev/null +++ b/test/fixtures/app-information/ui-imported-source/project/extensions/ui/src/helper.ts @@ -0,0 +1 @@ +export const helper = true diff --git a/test/fixtures/app-information/ui-imported-source/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-imported-source/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..069f857686e --- /dev/null +++ b/test/fixtures/app-information/ui-imported-source/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +import "./helper.js"; diff --git a/test/fixtures/app-information/ui-imported-source/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-imported-source/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-imported-source/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-imported-source/project/package.json b/test/fixtures/app-information/ui-imported-source/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-imported-source/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-imported-source/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-imported-source/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-imported-source/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-imported-source/project/shopify.app.toml b/test/fixtures/app-information/ui-imported-source/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-imported-source/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-imported-source/project/web/shopify.web.toml b/test/fixtures/app-information/ui-imported-source/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-imported-source/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/intent.json b/test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/intent.json new file mode 100644 index 00000000000..d3db885cc09 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/intent.json @@ -0,0 +1 @@ +{bad \ No newline at end of file diff --git a/test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..26f736808f6 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,13 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" +[[extensions.targeting.intents]] +action = "edit" +type = "fixture" +schema = "./intent.json" diff --git a/test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-json/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-intent-invalid-json/project/package.json b/test/fixtures/app-information/ui-intent-invalid-json/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-json/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-intent-invalid-json/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-intent-invalid-json/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-json/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-intent-invalid-json/project/shopify.app.toml b/test/fixtures/app-information/ui-intent-invalid-json/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-json/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-intent-invalid-json/project/web/shopify.web.toml b/test/fixtures/app-information/ui-intent-invalid-json/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-json/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/intent.json b/test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/intent.json new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/intent.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..26f736808f6 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,13 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" +[[extensions.targeting.intents]] +action = "edit" +type = "fixture" +schema = "./intent.json" diff --git a/test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-shape/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-intent-invalid-shape/project/package.json b/test/fixtures/app-information/ui-intent-invalid-shape/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-shape/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-intent-invalid-shape/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-intent-invalid-shape/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-shape/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-intent-invalid-shape/project/shopify.app.toml b/test/fixtures/app-information/ui-intent-invalid-shape/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-shape/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-intent-invalid-shape/project/web/shopify.web.toml b/test/fixtures/app-information/ui-intent-invalid-shape/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-intent-invalid-shape/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..26f736808f6 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,13 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" +[[extensions.targeting.intents]] +action = "edit" +type = "fixture" +schema = "./intent.json" diff --git a/test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-missing-file/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-intent-missing-file/project/package.json b/test/fixtures/app-information/ui-intent-missing-file/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-missing-file/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-intent-missing-file/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-intent-missing-file/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-missing-file/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-intent-missing-file/project/shopify.app.toml b/test/fixtures/app-information/ui-intent-missing-file/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-missing-file/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-intent-missing-file/project/web/shopify.web.toml b/test/fixtures/app-information/ui-intent-missing-file/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-intent-missing-file/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-intent-valid/project/extensions/ui/intent.json b/test/fixtures/app-information/ui-intent-valid/project/extensions/ui/intent.json new file mode 100644 index 00000000000..c1757c5f76d --- /dev/null +++ b/test/fixtures/app-information/ui-intent-valid/project/extensions/ui/intent.json @@ -0,0 +1 @@ +{"inputSchema":{"type":"object","properties":{"inputValue":{"type":"string"}}},"value":{"type":"object","properties":{"selectedValue":{"type":"number"}}},"outputSchema":{"type":"boolean"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-intent-valid/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-intent-valid/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-intent-valid/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-intent-valid/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-intent-valid/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-valid/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-intent-valid/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-intent-valid/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..26f736808f6 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-valid/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,13 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" +[[extensions.targeting.intents]] +action = "edit" +type = "fixture" +schema = "./intent.json" diff --git a/test/fixtures/app-information/ui-intent-valid/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-intent-valid/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-valid/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-intent-valid/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-intent-valid/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-valid/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-intent-valid/project/package.json b/test/fixtures/app-information/ui-intent-valid/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-valid/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-intent-valid/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-intent-valid/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-valid/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-intent-valid/project/shopify.app.toml b/test/fixtures/app-information/ui-intent-valid/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-intent-valid/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-intent-valid/project/web/shopify.web.toml b/test/fixtures/app-information/ui-intent-valid/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-intent-valid/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..5b8a35b54c8 --- /dev/null +++ b/test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,10 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" +tools = "./tools.json" diff --git a/test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/tools.json b/test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/tools.json new file mode 100644 index 00000000000..40e81c34ce8 --- /dev/null +++ b/test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/tools.json @@ -0,0 +1 @@ +[{"name":"lookup","description":"","inputSchema":{"$ref":"./input.json"}}] \ No newline at end of file diff --git a/test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-local-schema-reference/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-local-schema-reference/project/input.json b/test/fixtures/app-information/ui-local-schema-reference/project/input.json new file mode 100644 index 00000000000..801bf50eec7 --- /dev/null +++ b/test/fixtures/app-information/ui-local-schema-reference/project/input.json @@ -0,0 +1 @@ +{"type":"object","properties":{"rootValue":{"type":"string"}}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-local-schema-reference/project/package.json b/test/fixtures/app-information/ui-local-schema-reference/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-local-schema-reference/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-local-schema-reference/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-local-schema-reference/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-local-schema-reference/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-local-schema-reference/project/shopify.app.toml b/test/fixtures/app-information/ui-local-schema-reference/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-local-schema-reference/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-local-schema-reference/project/web/shopify.web.toml b/test/fixtures/app-information/ui-local-schema-reference/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-local-schema-reference/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..65306b8d2ee --- /dev/null +++ b/test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{"./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..5b8a35b54c8 --- /dev/null +++ b/test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,10 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" +tools = "./tools.json" diff --git a/test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/tools.json b/test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/tools.json new file mode 100644 index 00000000000..049004efc3f --- /dev/null +++ b/test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/tools.json @@ -0,0 +1 @@ +[{"name":"lookup","description":"","inputSchema":{"type":"object"}}] \ No newline at end of file diff --git a/test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-missing-helper-export/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-missing-helper-export/project/package.json b/test/fixtures/app-information/ui-missing-helper-export/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-missing-helper-export/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-missing-helper-export/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-missing-helper-export/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-missing-helper-export/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-missing-helper-export/project/shopify.app.toml b/test/fixtures/app-information/ui-missing-helper-export/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-missing-helper-export/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-missing-helper-export/project/web/shopify.web.toml b/test/fixtures/app-information/ui-missing-helper-export/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-missing-helper-export/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-missing-source/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-missing-source/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..4e3fe03106e --- /dev/null +++ b/test/fixtures/app-information/ui-missing-source/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,9 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" diff --git a/test/fixtures/app-information/ui-missing-source/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-missing-source/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-missing-source/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-missing-source/project/package.json b/test/fixtures/app-information/ui-missing-source/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-missing-source/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-missing-source/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-missing-source/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-missing-source/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-missing-source/project/shopify.app.toml b/test/fixtures/app-information/ui-missing-source/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-missing-source/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-missing-source/project/web/shopify.web.toml b/test/fixtures/app-information/ui-missing-source/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-missing-source/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..2c2b033c92b --- /dev/null +++ b/test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..4e3fe03106e --- /dev/null +++ b/test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,9 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" diff --git a/test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-missing-target-export/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-missing-target-export/project/package.json b/test/fixtures/app-information/ui-missing-target-export/project/package.json new file mode 100644 index 00000000000..2ff1f11ff70 --- /dev/null +++ b/test/fixtures/app-information/ui-missing-target-export/project/package.json @@ -0,0 +1 @@ +{"dependencies":{"@shopify/ui-extensions":"~2025.10.0"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-missing-target-export/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-missing-target-export/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-missing-target-export/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-missing-target-export/project/shopify.app.toml b/test/fixtures/app-information/ui-missing-target-export/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-missing-target-export/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-missing-target-export/project/web/shopify.web.toml b/test/fixtures/app-information/ui-missing-target-export/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-missing-target-export/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-no-installed-package/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-no-installed-package/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..4e3fe03106e --- /dev/null +++ b/test/fixtures/app-information/ui-no-installed-package/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,9 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" diff --git a/test/fixtures/app-information/ui-no-installed-package/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-no-installed-package/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-no-installed-package/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-no-installed-package/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-no-installed-package/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-no-installed-package/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-no-installed-package/project/package.json b/test/fixtures/app-information/ui-no-installed-package/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-no-installed-package/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-no-installed-package/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-no-installed-package/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-no-installed-package/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-no-installed-package/project/shopify.app.toml b/test/fixtures/app-information/ui-no-installed-package/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-no-installed-package/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-no-installed-package/project/web/shopify.web.toml b/test/fixtures/app-information/ui-no-installed-package/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-no-installed-package/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/extensions/ui/shopify.d.ts b/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/extensions/ui/shopify.d.ts new file mode 100644 index 00000000000..c7c4f715175 --- /dev/null +++ b/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/extensions/ui/shopify.d.ts @@ -0,0 +1 @@ +stale declarations diff --git a/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..61008324034 --- /dev/null +++ b/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,14 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" +tools = "./tools.json" +[[extensions.targeting.intents]] +action = "edit" +type = "fixture" +schema = "./intent.json" diff --git a/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/package.json b/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/shopify.app.toml b/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/web/shopify.web.toml b/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-no-tsconfig-stale-types/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-no-uid/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-no-uid/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-no-uid/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-no-uid/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-no-uid/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-no-uid/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-no-uid/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-no-uid/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..a2ed81e34e9 --- /dev/null +++ b/test/fixtures/app-information/ui-no-uid/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,8 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" diff --git a/test/fixtures/app-information/ui-no-uid/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-no-uid/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-no-uid/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-no-uid/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-no-uid/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-no-uid/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-no-uid/project/package.json b/test/fixtures/app-information/ui-no-uid/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-no-uid/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-no-uid/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-no-uid/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-no-uid/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-no-uid/project/shopify.app.toml b/test/fixtures/app-information/ui-no-uid/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-no-uid/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-no-uid/project/web/shopify.web.toml b/test/fixtures/app-information/ui-no-uid/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-no-uid/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-old-api-stale-types/project/extensions/ui/shopify.d.ts b/test/fixtures/app-information/ui-old-api-stale-types/project/extensions/ui/shopify.d.ts new file mode 100644 index 00000000000..c7c4f715175 --- /dev/null +++ b/test/fixtures/app-information/ui-old-api-stale-types/project/extensions/ui/shopify.d.ts @@ -0,0 +1 @@ +stale declarations diff --git a/test/fixtures/app-information/ui-old-api-stale-types/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-old-api-stale-types/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..f5cd7967221 --- /dev/null +++ b/test/fixtures/app-information/ui-old-api-stale-types/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,14 @@ +api_version = "2025-07" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" +tools = "./tools.json" +[[extensions.targeting.intents]] +action = "edit" +type = "fixture" +schema = "./intent.json" diff --git a/test/fixtures/app-information/ui-old-api-stale-types/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-old-api-stale-types/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-old-api-stale-types/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-old-api-stale-types/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-old-api-stale-types/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-old-api-stale-types/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-old-api-stale-types/project/package.json b/test/fixtures/app-information/ui-old-api-stale-types/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-old-api-stale-types/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-old-api-stale-types/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-old-api-stale-types/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-old-api-stale-types/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-old-api-stale-types/project/shopify.app.toml b/test/fixtures/app-information/ui-old-api-stale-types/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-old-api-stale-types/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-old-api-stale-types/project/web/shopify.web.toml b/test/fixtures/app-information/ui-old-api-stale-types/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-old-api-stale-types/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-output-directory/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-output-directory/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-output-directory/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-output-directory/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-output-directory/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-output-directory/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-output-directory/project/extensions/ui/shopify.d.ts/.gitkeep b/test/fixtures/app-information/ui-output-directory/project/extensions/ui/shopify.d.ts/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/fixtures/app-information/ui-output-directory/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-output-directory/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..4e3fe03106e --- /dev/null +++ b/test/fixtures/app-information/ui-output-directory/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,9 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" diff --git a/test/fixtures/app-information/ui-output-directory/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-output-directory/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-output-directory/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-output-directory/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-output-directory/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-output-directory/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-output-directory/project/package.json b/test/fixtures/app-information/ui-output-directory/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-output-directory/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-output-directory/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-output-directory/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-output-directory/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-output-directory/project/shopify.app.toml b/test/fixtures/app-information/ui-output-directory/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-output-directory/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-output-directory/project/web/shopify.web.toml b/test/fixtures/app-information/ui-output-directory/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-output-directory/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-shopify-global/project/extensions/ui/node_modules/@shopify/ui-extensions/globals.d.ts b/test/fixtures/app-information/ui-shopify-global/project/extensions/ui/node_modules/@shopify/ui-extensions/globals.d.ts new file mode 100644 index 00000000000..083b31ad407 --- /dev/null +++ b/test/fixtures/app-information/ui-shopify-global/project/extensions/ui/node_modules/@shopify/ui-extensions/globals.d.ts @@ -0,0 +1 @@ +export interface ShopifyGlobal {fixtureValue: string} diff --git a/test/fixtures/app-information/ui-shopify-global/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-shopify-global/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..b1e0cf9a0b4 --- /dev/null +++ b/test/fixtures/app-information/ui-shopify-global/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1,2 @@ +export interface Api {} +export type {ShopifyGlobal} from "./globals.js"; diff --git a/test/fixtures/app-information/ui-shopify-global/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-shopify-global/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-shopify-global/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-shopify-global/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-shopify-global/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..4e3fe03106e --- /dev/null +++ b/test/fixtures/app-information/ui-shopify-global/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,9 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" diff --git a/test/fixtures/app-information/ui-shopify-global/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-shopify-global/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-shopify-global/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-shopify-global/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-shopify-global/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-shopify-global/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-shopify-global/project/package.json b/test/fixtures/app-information/ui-shopify-global/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-shopify-global/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-shopify-global/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-shopify-global/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-shopify-global/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-shopify-global/project/shopify.app.toml b/test/fixtures/app-information/ui-shopify-global/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-shopify-global/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-shopify-global/project/web/shopify.web.toml b/test/fixtures/app-information/ui-shopify-global/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-shopify-global/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..5b8a35b54c8 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,10 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" +tools = "./tools.json" diff --git a/test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/tools.json b/test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/tools.json new file mode 100644 index 00000000000..e7c08f0b124 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/tools.json @@ -0,0 +1 @@ +[{"name":"same","description":"","inputSchema":{}},{"name":"same","description":"","inputSchema":{}}] \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-duplicate-names/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-duplicate-names/project/package.json b/test/fixtures/app-information/ui-tools-duplicate-names/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-duplicate-names/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-duplicate-names/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-tools-duplicate-names/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-duplicate-names/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-tools-duplicate-names/project/shopify.app.toml b/test/fixtures/app-information/ui-tools-duplicate-names/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-duplicate-names/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-tools-duplicate-names/project/web/shopify.web.toml b/test/fixtures/app-information/ui-tools-duplicate-names/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-tools-duplicate-names/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-tools-empty/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-tools-empty/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-tools-empty/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-tools-empty/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-tools-empty/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-empty/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-empty/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-tools-empty/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..5b8a35b54c8 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-empty/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,10 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" +tools = "./tools.json" diff --git a/test/fixtures/app-information/ui-tools-empty/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-tools-empty/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-empty/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-tools-empty/project/extensions/ui/tools.json b/test/fixtures/app-information/ui-tools-empty/project/extensions/ui/tools.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-empty/project/extensions/ui/tools.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-empty/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-tools-empty/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-empty/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-empty/project/package.json b/test/fixtures/app-information/ui-tools-empty/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-empty/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-empty/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-tools-empty/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-empty/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-tools-empty/project/shopify.app.toml b/test/fixtures/app-information/ui-tools-empty/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-empty/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-tools-empty/project/web/shopify.web.toml b/test/fixtures/app-information/ui-tools-empty/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-tools-empty/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..5b8a35b54c8 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,10 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" +tools = "./tools.json" diff --git a/test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/tools.json b/test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/tools.json new file mode 100644 index 00000000000..d3db885cc09 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/tools.json @@ -0,0 +1 @@ +{bad \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-json/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-invalid-json/project/package.json b/test/fixtures/app-information/ui-tools-invalid-json/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-json/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-invalid-json/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-tools-invalid-json/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-json/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-tools-invalid-json/project/shopify.app.toml b/test/fixtures/app-information/ui-tools-invalid-json/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-json/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-tools-invalid-json/project/web/shopify.web.toml b/test/fixtures/app-information/ui-tools-invalid-json/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-json/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..5b8a35b54c8 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,10 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" +tools = "./tools.json" diff --git a/test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/tools.json b/test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/tools.json new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/tools.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-shape/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-invalid-shape/project/package.json b/test/fixtures/app-information/ui-tools-invalid-shape/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-shape/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-invalid-shape/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-tools-invalid-shape/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-shape/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-tools-invalid-shape/project/shopify.app.toml b/test/fixtures/app-information/ui-tools-invalid-shape/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-shape/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-tools-invalid-shape/project/web/shopify.web.toml b/test/fixtures/app-information/ui-tools-invalid-shape/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-tools-invalid-shape/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..5b8a35b54c8 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,10 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" +tools = "./tools.json" diff --git a/test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-missing-file/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-missing-file/project/package.json b/test/fixtures/app-information/ui-tools-missing-file/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-missing-file/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-missing-file/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-tools-missing-file/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-missing-file/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-tools-missing-file/project/shopify.app.toml b/test/fixtures/app-information/ui-tools-missing-file/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-missing-file/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-tools-missing-file/project/web/shopify.web.toml b/test/fixtures/app-information/ui-tools-missing-file/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-tools-missing-file/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-tools-valid/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-tools-valid/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-tools-valid/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-tools-valid/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-tools-valid/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-valid/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-valid/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-tools-valid/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..5b8a35b54c8 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-valid/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,10 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" +tools = "./tools.json" diff --git a/test/fixtures/app-information/ui-tools-valid/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-tools-valid/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-valid/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-tools-valid/project/extensions/ui/tools.json b/test/fixtures/app-information/ui-tools-valid/project/extensions/ui/tools.json new file mode 100644 index 00000000000..3022fbf871e --- /dev/null +++ b/test/fixtures/app-information/ui-tools-valid/project/extensions/ui/tools.json @@ -0,0 +1 @@ +[{"name":"lookup","description":"Find a fixture","inputSchema":{"type":"object","properties":{"id":{"type":"string"}}}}] \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-valid/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-tools-valid/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-valid/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-valid/project/package.json b/test/fixtures/app-information/ui-tools-valid/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-valid/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-tools-valid/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-tools-valid/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-valid/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-tools-valid/project/shopify.app.toml b/test/fixtures/app-information/ui-tools-valid/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-tools-valid/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-tools-valid/project/web/shopify.web.toml b/test/fixtures/app-information/ui-tools-valid/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-tools-valid/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts b/test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts new file mode 100644 index 00000000000..cca64538adf --- /dev/null +++ b/test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/node_modules/@shopify/ui-extensions/index.d.ts @@ -0,0 +1 @@ +export interface Api {} diff --git a/test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json b/test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json new file mode 100644 index 00000000000..7918a717883 --- /dev/null +++ b/test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/node_modules/@shopify/ui-extensions/package.json @@ -0,0 +1 @@ +{"name":"@shopify/ui-extensions","version":"2025.10.0","exports":{".":"./index.d.ts","./admin":"./index.d.ts","./admin.product-details.action.render":"./index.d.ts"}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/shopify.extension.toml b/test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/shopify.extension.toml new file mode 100644 index 00000000000..5b8a35b54c8 --- /dev/null +++ b/test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/shopify.extension.toml @@ -0,0 +1,10 @@ +api_version = "2025-10" +[[extensions]] +name = "Fixture UI" +handle = "fixture-ui" +type = "ui_extension" +uid = "11111111-1111-1111-1111-111111111111" +[[extensions.targeting]] +target = "admin.product-details.action.render" +module = "./src/index.ts" +tools = "./tools.json" diff --git a/test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/src/index.ts b/test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/src/index.ts new file mode 100644 index 00000000000..8b682c74ce9 --- /dev/null +++ b/test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/src/index.ts @@ -0,0 +1 @@ +export default function extension() {} diff --git a/test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/tools.json b/test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/tools.json new file mode 100644 index 00000000000..66fd9a22f62 --- /dev/null +++ b/test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/tools.json @@ -0,0 +1 @@ +[{"name":"lookup","description":"","inputSchema":{"$ref":"https://unregistered.example.test/schema.json"}}] \ No newline at end of file diff --git a/test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/tsconfig.json b/test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/tsconfig.json new file mode 100644 index 00000000000..18daa1c1045 --- /dev/null +++ b/test/fixtures/app-information/ui-unregistered-schema/project/extensions/ui/tsconfig.json @@ -0,0 +1 @@ +{"include":["src/**/*"]} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-unregistered-schema/project/package.json b/test/fixtures/app-information/ui-unregistered-schema/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/ui-unregistered-schema/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/ui-unregistered-schema/project/pnpm-lock.yaml b/test/fixtures/app-information/ui-unregistered-schema/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/ui-unregistered-schema/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/ui-unregistered-schema/project/shopify.app.toml b/test/fixtures/app-information/ui-unregistered-schema/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/ui-unregistered-schema/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/ui-unregistered-schema/project/web/shopify.web.toml b/test/fixtures/app-information/ui-unregistered-schema/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/ui-unregistered-schema/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/unlinked-client-id/project/package.json b/test/fixtures/app-information/unlinked-client-id/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/unlinked-client-id/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/unlinked-client-id/project/pnpm-lock.yaml b/test/fixtures/app-information/unlinked-client-id/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/unlinked-client-id/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/unlinked-client-id/project/shopify.app.toml b/test/fixtures/app-information/unlinked-client-id/project/shopify.app.toml new file mode 100644 index 00000000000..c4466f93d2a --- /dev/null +++ b/test/fixtures/app-information/unlinked-client-id/project/shopify.app.toml @@ -0,0 +1 @@ +client_id = "" diff --git a/test/fixtures/app-information/unlinked-client-id/project/web/shopify.web.toml b/test/fixtures/app-information/unlinked-client-id/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/unlinked-client-id/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/unlinked-named-template/project/package.json b/test/fixtures/app-information/unlinked-named-template/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/unlinked-named-template/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/unlinked-named-template/project/pnpm-lock.yaml b/test/fixtures/app-information/unlinked-named-template/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/unlinked-named-template/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/unlinked-named-template/project/shopify.app.toml b/test/fixtures/app-information/unlinked-named-template/project/shopify.app.toml new file mode 100644 index 00000000000..0d2a416ae7f --- /dev/null +++ b/test/fixtures/app-information/unlinked-named-template/project/shopify.app.toml @@ -0,0 +1 @@ +name = "Unlinked fixture" diff --git a/test/fixtures/app-information/unlinked-named-template/project/web/shopify.web.toml b/test/fixtures/app-information/unlinked-named-template/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/unlinked-named-template/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/web-both-branches-invalid/project/package.json b/test/fixtures/app-information/web-both-branches-invalid/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/web-both-branches-invalid/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/web-both-branches-invalid/project/pnpm-lock.yaml b/test/fixtures/app-information/web-both-branches-invalid/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/web-both-branches-invalid/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/web-both-branches-invalid/project/shopify.app.toml b/test/fixtures/app-information/web-both-branches-invalid/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/web-both-branches-invalid/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/web-both-branches-invalid/project/web/shopify.web.toml b/test/fixtures/app-information/web-both-branches-invalid/project/web/shopify.web.toml new file mode 100644 index 00000000000..2cd1a10cb87 --- /dev/null +++ b/test/fixtures/app-information/web-both-branches-invalid/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +roles = ["invalid"] +type = "invalid" +[commands] +dev = "never-run" diff --git a/test/fixtures/app-information/web-default-frontend/project/package.json b/test/fixtures/app-information/web-default-frontend/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/web-default-frontend/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/web-default-frontend/project/pnpm-lock.yaml b/test/fixtures/app-information/web-default-frontend/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/web-default-frontend/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/web-default-frontend/project/shopify.app.toml b/test/fixtures/app-information/web-default-frontend/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/web-default-frontend/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/web-default-frontend/project/web/shopify.web.toml b/test/fixtures/app-information/web-default-frontend/project/web/shopify.web.toml new file mode 100644 index 00000000000..46545448ccf --- /dev/null +++ b/test/fixtures/app-information/web-default-frontend/project/web/shopify.web.toml @@ -0,0 +1,2 @@ +[commands] +dev = "never-run" diff --git a/test/fixtures/app-information/web-empty-roles/project/package.json b/test/fixtures/app-information/web-empty-roles/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/web-empty-roles/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/web-empty-roles/project/pnpm-lock.yaml b/test/fixtures/app-information/web-empty-roles/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/web-empty-roles/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/web-empty-roles/project/shopify.app.toml b/test/fixtures/app-information/web-empty-roles/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/web-empty-roles/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/web-empty-roles/project/web/shopify.web.toml b/test/fixtures/app-information/web-empty-roles/project/web/shopify.web.toml new file mode 100644 index 00000000000..15b94b9a385 --- /dev/null +++ b/test/fixtures/app-information/web-empty-roles/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +roles = [] +type = "frontend" +[commands] +dev = "never-run" diff --git a/test/fixtures/app-information/web-hmr-missing-paths/project/package.json b/test/fixtures/app-information/web-hmr-missing-paths/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/web-hmr-missing-paths/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/web-hmr-missing-paths/project/pnpm-lock.yaml b/test/fixtures/app-information/web-hmr-missing-paths/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/web-hmr-missing-paths/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/web-hmr-missing-paths/project/shopify.app.toml b/test/fixtures/app-information/web-hmr-missing-paths/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/web-hmr-missing-paths/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/web-hmr-missing-paths/project/web/shopify.web.toml b/test/fixtures/app-information/web-hmr-missing-paths/project/web/shopify.web.toml new file mode 100644 index 00000000000..190b82c5cf8 --- /dev/null +++ b/test/fixtures/app-information/web-hmr-missing-paths/project/web/shopify.web.toml @@ -0,0 +1,5 @@ +roles = ["frontend"] +[hmr_server] +port = 8000 +[commands] +dev = "never-run" diff --git a/test/fixtures/app-information/web-invalid-command/project/package.json b/test/fixtures/app-information/web-invalid-command/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/web-invalid-command/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/web-invalid-command/project/pnpm-lock.yaml b/test/fixtures/app-information/web-invalid-command/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/web-invalid-command/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/web-invalid-command/project/shopify.app.toml b/test/fixtures/app-information/web-invalid-command/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/web-invalid-command/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/web-invalid-command/project/web/shopify.web.toml b/test/fixtures/app-information/web-invalid-command/project/web/shopify.web.toml new file mode 100644 index 00000000000..be755c22b37 --- /dev/null +++ b/test/fixtures/app-information/web-invalid-command/project/web/shopify.web.toml @@ -0,0 +1,3 @@ +roles = ["frontend"] +[commands] +dev = 42 \ No newline at end of file diff --git a/test/fixtures/app-information/web-invalid-roles-fallback/project/package.json b/test/fixtures/app-information/web-invalid-roles-fallback/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/web-invalid-roles-fallback/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/web-invalid-roles-fallback/project/pnpm-lock.yaml b/test/fixtures/app-information/web-invalid-roles-fallback/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/web-invalid-roles-fallback/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/web-invalid-roles-fallback/project/shopify.app.toml b/test/fixtures/app-information/web-invalid-roles-fallback/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/web-invalid-roles-fallback/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/web-invalid-roles-fallback/project/web/shopify.web.toml b/test/fixtures/app-information/web-invalid-roles-fallback/project/web/shopify.web.toml new file mode 100644 index 00000000000..5d3829b831e --- /dev/null +++ b/test/fixtures/app-information/web-invalid-roles-fallback/project/web/shopify.web.toml @@ -0,0 +1,5 @@ +roles = ["invalid"] +type = "background" +auth_callback_path = "callback" +[commands] +dev = "never-run" diff --git a/test/fixtures/app-information/web-missing-command/project/package.json b/test/fixtures/app-information/web-missing-command/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/web-missing-command/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/web-missing-command/project/pnpm-lock.yaml b/test/fixtures/app-information/web-missing-command/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/web-missing-command/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/web-missing-command/project/shopify.app.toml b/test/fixtures/app-information/web-missing-command/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/web-missing-command/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/web-missing-command/project/web/shopify.web.toml b/test/fixtures/app-information/web-missing-command/project/web/shopify.web.toml new file mode 100644 index 00000000000..3c290a220ab --- /dev/null +++ b/test/fixtures/app-information/web-missing-command/project/web/shopify.web.toml @@ -0,0 +1 @@ +roles = ["frontend"] \ No newline at end of file diff --git a/test/fixtures/app-information/web-port-fraction/project/package.json b/test/fixtures/app-information/web-port-fraction/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/web-port-fraction/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/web-port-fraction/project/pnpm-lock.yaml b/test/fixtures/app-information/web-port-fraction/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/web-port-fraction/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/web-port-fraction/project/shopify.app.toml b/test/fixtures/app-information/web-port-fraction/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/web-port-fraction/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/web-port-fraction/project/web/shopify.web.toml b/test/fixtures/app-information/web-port-fraction/project/web/shopify.web.toml new file mode 100644 index 00000000000..4b329639339 --- /dev/null +++ b/test/fixtures/app-information/web-port-fraction/project/web/shopify.web.toml @@ -0,0 +1,3 @@ +port = 1.5 +[commands] +dev = "never-run" diff --git a/test/fixtures/app-information/web-port-negative/project/package.json b/test/fixtures/app-information/web-port-negative/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/web-port-negative/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/web-port-negative/project/pnpm-lock.yaml b/test/fixtures/app-information/web-port-negative/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/web-port-negative/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/web-port-negative/project/shopify.app.toml b/test/fixtures/app-information/web-port-negative/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/web-port-negative/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/web-port-negative/project/web/shopify.web.toml b/test/fixtures/app-information/web-port-negative/project/web/shopify.web.toml new file mode 100644 index 00000000000..da0024cd243 --- /dev/null +++ b/test/fixtures/app-information/web-port-negative/project/web/shopify.web.toml @@ -0,0 +1,3 @@ +port = -1 +[commands] +dev = "never-run" diff --git a/test/fixtures/app-information/web-port-string/project/package.json b/test/fixtures/app-information/web-port-string/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/web-port-string/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/web-port-string/project/pnpm-lock.yaml b/test/fixtures/app-information/web-port-string/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/web-port-string/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/web-port-string/project/shopify.app.toml b/test/fixtures/app-information/web-port-string/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/web-port-string/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/web-port-string/project/web/shopify.web.toml b/test/fixtures/app-information/web-port-string/project/web/shopify.web.toml new file mode 100644 index 00000000000..e731ce58ad4 --- /dev/null +++ b/test/fixtures/app-information/web-port-string/project/web/shopify.web.toml @@ -0,0 +1,3 @@ +port = "3000" +[commands] +dev = "never-run" diff --git a/test/fixtures/app-information/web-port-too-large/project/package.json b/test/fixtures/app-information/web-port-too-large/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/web-port-too-large/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/web-port-too-large/project/pnpm-lock.yaml b/test/fixtures/app-information/web-port-too-large/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/web-port-too-large/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/web-port-too-large/project/shopify.app.toml b/test/fixtures/app-information/web-port-too-large/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/web-port-too-large/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/web-port-too-large/project/web/shopify.web.toml b/test/fixtures/app-information/web-port-too-large/project/web/shopify.web.toml new file mode 100644 index 00000000000..fa440313d5f --- /dev/null +++ b/test/fixtures/app-information/web-port-too-large/project/web/shopify.web.toml @@ -0,0 +1,3 @@ +port = 65537 +[commands] +dev = "never-run" diff --git a/test/fixtures/app-information/web-port-zero/project/package.json b/test/fixtures/app-information/web-port-zero/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/web-port-zero/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/web-port-zero/project/pnpm-lock.yaml b/test/fixtures/app-information/web-port-zero/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/web-port-zero/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/web-port-zero/project/shopify.app.toml b/test/fixtures/app-information/web-port-zero/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/web-port-zero/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/web-port-zero/project/web/shopify.web.toml b/test/fixtures/app-information/web-port-zero/project/web/shopify.web.toml new file mode 100644 index 00000000000..54a7cd8a892 --- /dev/null +++ b/test/fixtures/app-information/web-port-zero/project/web/shopify.web.toml @@ -0,0 +1,3 @@ +port = 0 +[commands] +dev = "never-run" diff --git a/test/fixtures/app-information/web-remix-framework/project/package.json b/test/fixtures/app-information/web-remix-framework/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/web-remix-framework/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/web-remix-framework/project/pnpm-lock.yaml b/test/fixtures/app-information/web-remix-framework/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/web-remix-framework/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/web-remix-framework/project/shopify.app.toml b/test/fixtures/app-information/web-remix-framework/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/web-remix-framework/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/web-remix-framework/project/web/package.json b/test/fixtures/app-information/web-remix-framework/project/web/package.json new file mode 100644 index 00000000000..c1199ec65f1 --- /dev/null +++ b/test/fixtures/app-information/web-remix-framework/project/web/package.json @@ -0,0 +1 @@ +{"dependencies":{"@remix-run/react":"2.0.0","react":"18.0.0"}} \ No newline at end of file diff --git a/test/fixtures/app-information/web-remix-framework/project/web/shopify.web.toml b/test/fixtures/app-information/web-remix-framework/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/web-remix-framework/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/fixtures/app-information/web-roles-win/project/package.json b/test/fixtures/app-information/web-roles-win/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/web-roles-win/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/web-roles-win/project/pnpm-lock.yaml b/test/fixtures/app-information/web-roles-win/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/web-roles-win/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/web-roles-win/project/shopify.app.toml b/test/fixtures/app-information/web-roles-win/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/web-roles-win/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/web-roles-win/project/web/shopify.web.toml b/test/fixtures/app-information/web-roles-win/project/web/shopify.web.toml new file mode 100644 index 00000000000..b2962e90c19 --- /dev/null +++ b/test/fixtures/app-information/web-roles-win/project/web/shopify.web.toml @@ -0,0 +1,12 @@ +roles = ["backend", "backend"] +type = "invalid" +auth_callback_path = ["auth/callback", "/second"] +webhooks_path = "hooks" +port = 65536 +unknown = "discard" +[hmr_server] +http_paths = ["hmr"] +[commands] +dev = "" +build = "never-build" +predev = "never-predev" diff --git a/test/fixtures/app-information/workspace-marker/project/package.json b/test/fixtures/app-information/workspace-marker/project/package.json new file mode 100644 index 00000000000..482acf215d1 --- /dev/null +++ b/test/fixtures/app-information/workspace-marker/project/package.json @@ -0,0 +1 @@ +{"private":true,"dependencies":{},"devDependencies":{}} \ No newline at end of file diff --git a/test/fixtures/app-information/workspace-marker/project/pnpm-lock.yaml b/test/fixtures/app-information/workspace-marker/project/pnpm-lock.yaml new file mode 100644 index 00000000000..b0a073a9c13 --- /dev/null +++ b/test/fixtures/app-information/workspace-marker/project/pnpm-lock.yaml @@ -0,0 +1 @@ +lockfileVersion: '9.0' diff --git a/test/fixtures/app-information/workspace-marker/project/pnpm-workspace.yaml b/test/fixtures/app-information/workspace-marker/project/pnpm-workspace.yaml new file mode 100644 index 00000000000..2c3ffcbd0a3 --- /dev/null +++ b/test/fixtures/app-information/workspace-marker/project/pnpm-workspace.yaml @@ -0,0 +1 @@ +not parsed by app info \ No newline at end of file diff --git a/test/fixtures/app-information/workspace-marker/project/shopify.app.toml b/test/fixtures/app-information/workspace-marker/project/shopify.app.toml new file mode 100644 index 00000000000..231dc175fb6 --- /dev/null +++ b/test/fixtures/app-information/workspace-marker/project/shopify.app.toml @@ -0,0 +1,10 @@ +client_id = "synthetic-client-id" +name = "Local fixture" +application_url = "https://fixture.example.test" +embedded = true + +[access_scopes] +scopes = "read_products" + +[auth] +redirect_urls = ["https://fixture.example.test/auth/callback"] diff --git a/test/fixtures/app-information/workspace-marker/project/web/shopify.web.toml b/test/fixtures/app-information/workspace-marker/project/web/shopify.web.toml new file mode 100644 index 00000000000..862166bc08e --- /dev/null +++ b/test/fixtures/app-information/workspace-marker/project/web/shopify.web.toml @@ -0,0 +1,4 @@ +name = "Fixture web" +roles = ["frontend", "backend"] +[commands] +dev = "node server.js" diff --git a/test/support/build.ts b/test/support/build.ts new file mode 100644 index 00000000000..c03c00d9efc --- /dev/null +++ b/test/support/build.ts @@ -0,0 +1,66 @@ +import {build} from 'esbuild' +// Build the real CLI without importing its runtime into the Vitest process. +// eslint-disable-next-line no-restricted-imports +import {execFile} from 'node:child_process' +import {mkdtemp, rm, writeFile} from 'node:fs/promises' +import {createRequire} from 'node:module' +import {tmpdir} from 'node:os' +// eslint-disable-next-line no-restricted-imports +import {resolve} from 'node:path' +import {fileURLToPath} from 'node:url' +import {promisify} from 'node:util' +import type {TestProject} from 'vitest/node' + +const repository = fileURLToPath(new URL('../../', import.meta.url)) + +export default async function setup(project: TestProject) { + // Build the real executable, including bundled command files which take precedence + // over package dist files. Building only TypeScript could leave an old bundle in use. + await promisify(execFile)('bash', ['node_modules/.bin/nx', 'run', 'cli:bundle'], { + cwd: repository, + env: {...process.env, NX_DAEMON: 'false'}, + timeout: 180000, + maxBuffer: 10 * 1024 * 1024, + }) + + const directory = await mkdtemp(resolve(tmpdir(), 'cli-command-build-')) + try { + const require = createRequire(resolve(repository, 'packages/cli-kit/package.json')) + // Reuse CLI-kit's existing MSW dev dependency instead of adding another version. + await build({ + entryPoints: [resolve(repository, 'test/support/preload.ts')], + outfile: resolve(directory, 'preload.mjs'), + bundle: true, + platform: 'node', + format: 'esm', + plugins: [ + { + name: 'external-msw', + setup(builder) { + builder.onResolve({filter: /^msw(?:\/node)?$/}, ({path}) => ({path: require.resolve(path), external: true})) + }, + }, + ], + }) + const subprocessStub = resolve(directory, 'subprocess.cjs') + await writeFile( + subprocessStub, + 'const response = JSON.parse(process.argv[2]); process.stdout.write(response.stdout || ""); process.stderr.write(response.stderr || ""); process.exitCode = response.exitCode || 0;\n', + ) + project.provide('subprocessStub', subprocessStub) + project.provide('commandPreload', resolve(directory, 'preload.mjs')) + project.provide('cliEntrypoint', resolve(repository, 'packages/cli/bin/run.js')) + return () => rm(directory, {recursive: true, force: true}) + } catch (error) { + await rm(directory, {recursive: true, force: true}) + throw error + } +} + +declare module 'vitest' { + export interface ProvidedContext { + commandPreload: string + cliEntrypoint: string + subprocessStub: string + } +} diff --git a/test/support/filesystem.ts b/test/support/filesystem.ts new file mode 100644 index 00000000000..850b4fe0265 --- /dev/null +++ b/test/support/filesystem.ts @@ -0,0 +1,98 @@ +import {cp, lstat, mkdir, readdir, readFile, readlink, realpath} from 'node:fs/promises' +// The fixture loader must not initialize CLI state in the test worker. +// eslint-disable-next-line no-restricted-imports +import {dirname, isAbsolute, relative, resolve, sep} from 'node:path' + +const harnessPaths = new Set([ + 'bin', + 'home', + 'tmp', + 'config', + 'cache', + 'data', + 'state', + 'appdata', + 'localappdata', + 'oclif', + 'node_modules', + 'state.json', + 'trace.jsonl', +]) + +function isWithin(directory: string, candidate: string) { + const path = relative(directory, candidate) + return path === '' || (!isAbsolute(path) && path !== '..' && !path.startsWith(`..${sep}`)) +} + +async function validateTree(directory: string, sourceRoot: string): Promise { + const entries = await readdir(directory, {withFileTypes: true}) + await Promise.all( + entries.map(async (entry) => { + const path = resolve(directory, entry.name) + if (directory === sourceRoot && harnessPaths.has(entry.name)) { + throw new Error( + `Fixture path ${entry.name} conflicts with harness state; use seedState or runtime configuration instead`, + ) + } + if (entry.isSymbolicLink()) { + const target = await readlink(path) + if (isAbsolute(target) || !isWithin(sourceRoot, resolve(dirname(path), target))) { + throw new Error(`Fixture symlink escapes its scenario: ${path} -> ${target}`) + } + } else if (entry.isDirectory()) { + await validateTree(path, sourceRoot) + } else if (!entry.isFile()) { + throw new Error(`Unsupported fixture entry: ${path}`) + } + }), + ) +} + +async function fingerprintTree(root: string, directory = root): Promise { + const entries = await readdir(directory, {withFileTypes: true}) + const values = await Promise.all( + entries.map(async (entry) => { + const path = resolve(directory, entry.name) + if (entry.isDirectory()) return fingerprintTree(root, path) + const stats = await lstat(path) + const contents = entry.isSymbolicLink() + ? `link:${await readlink(path)}` + : (await readFile(path)).toString('base64') + return [JSON.stringify([relative(root, path), stats.mode, stats.mtimeMs, contents])] + }), + ) + return values.flat().sort() +} + +export async function reserveFilesystem(options: {fixturesRoot: string; destination: string; name: string}) { + const fixturesRoot = await realpath(options.fixturesRoot) + const requested = resolve(fixturesRoot, options.name) + if (!options.name || isAbsolute(options.name) || requested === fixturesRoot || !isWithin(fixturesRoot, requested)) { + throw new Error(`Invalid fixture name: ${options.name}`) + } + const source = await realpath(requested) + if (!isWithin(fixturesRoot, source)) throw new Error(`Fixture directory escapes its catalog: ${options.name}`) + if (!(await lstat(resolve(source, 'project'))).isDirectory()) + throw new Error(`Fixture ${options.name} needs a project directory`) + await validateTree(source, source) + const fingerprint = JSON.stringify(await fingerprintTree(source)) + // A real copy prevents one test's writes from changing another test's inputs. + // Preserve relative symlinks verbatim; cp's default can point back into the source. + await mkdir(options.destination, {recursive: true}) + const entries = await readdir(source) + await Promise.all( + entries.map((entry) => + cp(resolve(source, entry), resolve(options.destination, entry), { + recursive: true, + force: false, + errorOnExist: true, + verbatimSymlinks: true, + }), + ), + ) + return async () => { + if (JSON.stringify(await fingerprintTree(source)) !== fingerprint) { + throw new Error(`Source fixture was modified during the test: ${options.name}`) + } + } +} diff --git a/test/support/fixture.ts b/test/support/fixture.ts new file mode 100644 index 00000000000..cad58ae53c5 --- /dev/null +++ b/test/support/fixture.ts @@ -0,0 +1,510 @@ +import {reserveFilesystem} from './filesystem.js' +import {runInBash} from './process.js' +import {seedStoredState} from './state.js' +import {test as baseTest, inject} from 'vitest' +import {mkdtemp, mkdir, readFile, readdir, readlink, realpath, rm, writeFile, chmod} from 'node:fs/promises' +import {tmpdir} from 'node:os' +// Native paths keep the worker independent of CLI singleton state. +// eslint-disable-next-line no-restricted-imports +import {basename, dirname, isAbsolute, join, relative, resolve, sep} from 'node:path' +import {fileURLToPath} from 'node:url' +import {isDeepStrictEqual} from 'node:util' +import type {StoredState} from './state.js' +import type {TerminalOptions} from './process.js' +import type {CommandEvent, CommandState, RequestFixture, SubprocessFixture} from './protocol.js' + +export const now = Date.parse('2029-12-01T00:00:00Z') +export const clientId = 'synthetic-client-id' +export const appManagementUrl = 'https://app.shopify.com/app_management/unstable/graphql.json' +export const businessPlatformUrl = 'https://destinations.shopifysvc.com/destinations/api/2020-07/graphql' +export const appAudience = '7ee65a63608843c577db8b23c4d7316ea0a01bd2f7594f8a9c06ea668c1b775c' +export const businessAudience = '32ff8ee5-82b8-4d93-9f8a-c6997cefb7dc' + +export function specification(identifier: string, experience = 'configuration', jsonSchema?: object) { + return { + identifier, + externalIdentifier: identifier, + name: identifier, + experience, + features: [], + uidStrategy: { + __typename: experience === 'configuration' ? 'UidStrategiesStatic' : 'UidStrategiesClientProvided', + appModuleLimit: 50, + isClientProvided: experience !== 'configuration', + }, + validationSchema: jsonSchema ? {jsonSchema: JSON.stringify(jsonSchema)} : null, + } +} +function session() { + const expiresAt = '2031-01-01T00:00:00Z' + return { + identity: { + accessToken: 'synthetic-identity-token', + refreshToken: 'synthetic-refresh-token', + expiresAt, + userId: 'synthetic-user', + alias: 'fixture', + scopes: [ + 'openid', + ...[ + 'shop.admin.graphql', + 'shop.admin.themes', + 'partners.collaborator-relationships.readonly', + 'shop.storefront-renderer.devtools', + 'partners.app.cli.access', + 'destinations.readonly', + 'organization.store-management', + 'organization.on-demand-user-access', + 'organization.apps.manage', + ].map((scope) => `https://api.shopify.com/auth/${scope}`), + ], + }, + applications: { + [appAudience]: { + accessToken: 'synthetic-app-token', + expiresAt, + scopes: ['https://api.shopify.com/auth/organization.apps.manage'], + }, + [businessAudience]: { + accessToken: 'synthetic-business-token', + expiresAt, + scopes: ['https://api.shopify.com/auth/destinations.readonly'], + }, + }, + } +} +function resolveStorePath(root: string, store: string) { + const name = `${store}-nodejs` + if (process.platform === 'darwin') return join(root, 'home/Library/Preferences', name, 'config.json') + if (process.platform === 'win32') return join(root, 'appdata', name, 'Config/config.json') + return join(root, 'config', name, 'config.json') +} +interface CommandResult { + stdout: string + stderr: string + exitCode: number | null + signal: NodeJS.Signals | null + events: CommandEvent[] + requests: CommandEvent[] + before: Record + after: Record + rootBefore: Record + rootAfter: Record + terminalOutput: string + answeredPrompts: string[] + timedOut: boolean + terminatedAfterStdout: boolean +} + +interface RuntimeConfiguration { + environment?: Record + cwd?: string + clock?: 'frozen' | 'advancing' + requestLimit?: number + loopbackPorts?: number[] +} + +type NetworkOverride = {operation: string} & Partial> + +export class CommandFixture { + readonly projectPath: string + + readonly user = session() + + readonly remote = { + app: { + id: 'gid://shopify/App/456', + key: clientId, + organizationId: '123', + activeRoot: {grantedShopifyApprovalScopes: [], clientCredentials: {secrets: [{key: 'synthetic-app-secret'}]}}, + activeRelease: { + id: 'gid://shopify/AppRelease/789', + version: { + name: 'Remote fixture', + appModules: [] as { + uuid: string + userIdentifier: string + handle: string + config: Record + specification: { + identifier: string + externalIdentifier: string + name: string + experience: string + managementExperience: string + } + }[], + }, + }, + }, + specifications: ['branding', 'app_home', 'app_access'].map((id) => specification(id)), + } + + readonly networkMocks: RequestFixture[] + + environment: Record = {} + + subprocesses: SubprocessFixture[] = [] + + clock: 'frozen' | 'advancing' | undefined + + requestLimit = 24 + + loopbackPorts: number[] = [] + + private readonly kitStorePath: string + + private readonly appStorePath: string + + private invocations = 0 + + private reservedName?: string + private reservationAttempted = false + + private runtimeCwd?: string + private checkSourceUnchanged?: () => Promise + + constructor(readonly root: string) { + this.projectPath = join(root, 'project') + this.kitStorePath = resolveStorePath(root, 'shopify-cli-kit') + this.appStorePath = resolveStorePath(root, 'shopify-cli-app') + this.networkMocks = [ + { + method: 'POST', + url: businessPlatformUrl, + operation: 'UserInfo', + variables: {}, + headers: {Authorization: 'Bearer synthetic-business-token'}, + responses: [ + { + body: { + data: { + currentUserAccount: { + uuid: 'synthetic-user', + email: 'fixture@example.test', + organizations: {nodes: [{name: 'Fixture organization'}]}, + }, + }, + }, + }, + ], + }, + { + method: 'POST', + url: appManagementUrl, + operation: 'ActiveAppReleaseFromApiKey', + variables: {apiKey: clientId}, + headers: {Authorization: 'Bearer synthetic-app-token'}, + responses: [{body: {data: {app: this.remote.app}}}], + }, + { + method: 'POST', + url: businessPlatformUrl, + operation: 'FindOrganization', + variables: {organizationId: Buffer.from('gid://organization/Organization/123').toString('base64')}, + headers: {Authorization: 'Bearer synthetic-business-token'}, + responses: [{body: {data: {currentUserAccount: {organization: {id: '123', name: 'Fixture organization'}}}}}], + }, + { + method: 'POST', + url: appManagementUrl, + operation: 'fetchSpecifications', + variables: {organizationId: 'gid://shopify/Organization/123'}, + headers: {Authorization: 'Bearer synthetic-app-token'}, + responses: [{body: {data: {specifications: this.remote.specifications}}}], + }, + ] + } + + async initialize() { + await this.seedState({ + authentication: { + currentSessionId: 'synthetic-user', + sessions: {'accounts.shopify.com': {'synthetic-user': this.user}}, + }, + cliPreferences: {autoUpgradeEnabled: false}, + caches: {'npm-package-@shopify/cli': {value: '4.8.0', timestamp: now}}, + }) + await this.writeFileAt(this.appStorePath, '{}') + // An unresolved fixture package must never fall through to host installations. + await this.writeFileAt( + join(this.root, 'node_modules/@shopify/ui-extensions/package.json'), + JSON.stringify({name: '@shopify/ui-extensions', exports: {}}), + ) + const shim = join(this.root, 'bin/shopify') + await this.writeFileAt( + shim, + '#!/usr/bin/env bash\nexec "$CLI_TEST_NODE" --import "$CLI_TEST_PRELOAD" "$CLI_TEST_ENTRYPOINT" "$@"\n', + ) + await chmod(shim, 0o755) + await Promise.all( + ['home', 'tmp', 'oclif/config', 'oclif/cache', 'oclif/data'].map((path) => + mkdir(join(this.root, path), {recursive: true}), + ), + ) + } + + async reserve(name: string) { + if (this.reservationAttempted) + throw new Error('Reserve one filesystem scenario per test; repeated commands reuse its temporary copy') + this.reservationAttempted = true + this.checkSourceUnchanged = await reserveFilesystem({ + fixturesRoot: fileURLToPath(new URL('../fixtures/app-information/', import.meta.url)), + destination: this.root, + name, + }) + this.reservedName = name + } + + async dispose() { + try { + await this.checkSourceUnchanged?.() + } finally { + await rm(this.root, {recursive: true, force: true}) + } + } + + path(path = '') { + this.requireReservation() + const resolved = resolve(this.projectPath, path) + this.requireSandboxPath(resolved) + return resolved + } + + async writeFile(path: string, contents: string | Uint8Array) { + await this.writeFileAt(this.path(path), contents) + } + + async writeFileAt(path: string, contents: string | Uint8Array) { + this.requireSandboxPath(path) + await mkdir(dirname(path), {recursive: true}) + await writeFile(path, contents) + } + + async removeFile(path: string) { + await rm(this.path(path), {recursive: true, force: true}) + } + + async readFile(path: string) { + return readFile(this.path(path), 'utf8') + } + + storePath(store: 'app' | 'cli-kit') { + return store === 'app' ? this.appStorePath : this.kitStorePath + } + + async readStore(store: 'app' | 'cli-kit'): Promise> { + return JSON.parse(await readFile(this.storePath(store), 'utf8')) + } + + async seedState(state: StoredState) { + await seedStoredState({project: this.projectPath, app: this.appStorePath, cliKit: this.kitStorePath}, state) + } + + configure(options: RuntimeConfiguration) { + Object.assign(this.environment, options.environment) + if (options.cwd !== undefined) this.runtimeCwd = options.cwd + if (options.clock !== undefined) this.clock = options.clock + if (options.requestLimit !== undefined) this.requestLimit = options.requestLimit + if (options.loopbackPorts !== undefined) this.loopbackPorts = [...options.loopbackPorts] + } + + mockNetwork(...mocks: (RequestFixture | NetworkOverride)[]) { + for (const mock of mocks) { + if ('url' in mock) { + const duplicate = this.networkMocks.some( + (existing) => + existing.method === mock.method && + existing.url === mock.url && + existing.operation === mock.operation && + isDeepStrictEqual(existing.variables, mock.variables) && + isDeepStrictEqual(existing.form, mock.form) && + isDeepStrictEqual(existing.headers, mock.headers), + ) + if (duplicate) throw new Error(`Duplicate network matcher: ${mock.method} ${mock.url}`) + this.networkMocks.push(mock) + } else { + // Operation-only setup is an explicit override of one existing GraphQL mock. + const matches = this.networkMocks.filter((request) => request.operation === mock.operation) + if (matches.length !== 1) throw new Error(`Expected one existing network mock for ${mock.operation}`) + Object.assign(matches[0]!, mock) + } + } + } + + mockSubprocess(...mocks: SubprocessFixture[]) { + this.subprocesses.push(...mocks) + } + + mockTokenExchange(audience: string, token: string) { + this.mockNetwork({ + method: 'POST', + url: 'https://accounts.shopify.com/oauth/token', + form: {grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange', audience}, + responses: [{body: {access_token: token, expires_in: 3600, scope: this.user.identity.scopes.join(' ')}}], + }) + } + + async snapshot(root = this.projectPath) { + const files: Record = {} + const walk = async (directory: string): Promise => { + const entries = await readdir(directory, {withFileTypes: true}) + await Promise.all( + entries.map(async (entry) => { + if (directory === this.root && ['state.json', 'trace.jsonl', 'bin', 'node_modules'].includes(entry.name)) + return + const path = join(directory, entry.name) + const key = relative(root, path).split(sep).join('/') + if (entry.isDirectory()) await walk(path) + else if (entry.isFile()) files[key] = (await readFile(path)).toString('base64') + else if (entry.isSymbolicLink()) files[key] = `symlink:${await readlink(path)}` + }), + ) + } + await walk(root) + return files + } + + async runShopifyCommand( + argv: string[], + options: { + cwd?: string + stdin?: string + timeoutMs?: number + terminal?: TerminalOptions + allowTimeout?: boolean + terminateAfterStdout?: (stdout: string) => boolean + } = {}, + ): Promise { + this.requireReservation() + const coverageRoot = process.env.SHOPIFY_TEST_COVERAGE_DIR + const coverageDirectory = coverageRoot + ? resolve(coverageRoot, basename(this.root), String(this.invocations++)) + : undefined + if (coverageDirectory) await mkdir(coverageDirectory, {recursive: true}) + const state: CommandState = { + now, + requests: this.networkMocks, + requestLimit: this.requestLimit, + clock: this.clock ?? (options.terminal ? 'advancing' : 'frozen'), + subprocesses: [...this.subprocesses], + loopbackPorts: this.loopbackPorts, + } + if (options.terminal) { + // The interactive notification worker is a scripted child, not a real fetcher. + state.subprocesses.push({ + command: process.execPath, + args: [inject('cliEntrypoint'), 'notifications', 'list', '--ignore-errors'], + }) + } + const statePath = join(this.root, 'state.json') + const tracePath = join(this.root, 'trace.jsonl') + await writeFile(statePath, JSON.stringify(state)) + await writeFile(tracePath, '') + const before = await this.snapshot() + const rootBefore = await this.snapshot(this.root) + const env: Record = { + HOME: join(this.root, 'home'), + USERPROFILE: join(this.root, 'home'), + APPDATA: join(this.root, 'appdata'), + LOCALAPPDATA: join(this.root, 'localappdata'), + XDG_CONFIG_HOME: join(this.root, 'config'), + XDG_CACHE_HOME: join(this.root, 'cache'), + XDG_DATA_HOME: join(this.root, 'data'), + XDG_STATE_HOME: join(this.root, 'state'), + TMPDIR: join(this.root, 'tmp'), + TMP: join(this.root, 'tmp'), + TEMP: join(this.root, 'tmp'), + SHOPIFY_CONFIG_DIR: join(this.root, 'oclif/config'), + SHOPIFY_CACHE_DIR: join(this.root, 'oclif/cache'), + SHOPIFY_DATA_DIR: join(this.root, 'oclif/data'), + PATH: [join(this.root, 'bin'), dirname(process.execPath), '/usr/bin', '/bin'].join( + process.platform === 'win32' ? ';' : ':', + ), + SystemRoot: process.env.SystemRoot, + SHELL: '/bin/bash', + CI: options.terminal ? undefined : '1', + TERM: options.terminal ? 'xterm-256color' : 'dumb', + FORCE_COLOR: '0', + FORCE_HYPERLINK: '0', + SHOPIFY_CLI_COLUMNS: '160', + LANG: 'en_US.UTF-8', + LC_ALL: 'en_US.UTF-8', + TZ: 'UTC', + NODE_ENV: 'production', + NODE_DISABLE_COMPILE_CACHE: '1', + NODE_V8_COVERAGE: coverageDirectory, + // DEBUG='' enables every MSW logger; keep harness diagnostics out of CLI streams. + LOG_LEVEL: 'silent', + SHOPIFY_CLI_NO_ANALYTICS: '1', + OPT_OUT_INSTRUMENTATION: '1', + OCLIF_DISABLE_RC: '1', + SHOPIFY_CLI_SKIP_NETWORK_LEVEL_RETRY: '1', + SHOPIFY_CLI_MAX_REQUEST_TIME_FOR_NETWORK_CALLS: '1000', + ...this.environment, + CLI_TEST_SUBPROCESS: inject('subprocessStub'), + CLI_TEST_STATE: statePath, + CLI_TEST_TRACE: tracePath, + CLI_TEST_NODE: process.execPath, + CLI_TEST_PRELOAD: inject('commandPreload'), + CLI_TEST_ENTRYPOINT: inject('cliEntrypoint'), + } + // Arguments never become shell source. This really runs `shopify app info` in + // bash, through bin/run.js and all normal Oclif/bootstrap/lifecycle code. + const execution = await runInBash({ + env, + argv, + cwd: options.cwd ?? this.runtimeCwd ?? this.projectPath, + timeoutMs: options.timeoutMs ?? 12000, + terminal: options.terminal, + stdin: options.stdin, + terminateAfterStdout: options.terminateAfterStdout, + }) + const events: CommandEvent[] = (await readFile(tracePath, 'utf8')) + .trim() + .split('\n') + .filter(Boolean) + .map((line) => JSON.parse(line)) + const diagnostics = execution.stderr + execution.terminalOutput + if (execution.signal && !execution.timedOut && !execution.terminatedAfterStdout) + throw new Error(`CLI terminated by ${execution.signal}.\n${diagnostics}`) + if (execution.timedOut && !options.allowTimeout) throw new Error(`Command exceeded its deadline.\n${diagnostics}`) + if (!events.some((event) => event.type === 'ready')) + throw new Error(`Command interceptor did not start.\n${diagnostics}`) + const violations = events.filter((event) => event.type === 'violation') + if (violations.length) throw new Error(`Forbidden command activity: ${JSON.stringify(violations)}\n${diagnostics}`) + return { + ...execution, + events, + requests: events.filter((event) => event.type === 'request'), + before, + after: await this.snapshot(), + rootBefore, + rootAfter: await this.snapshot(this.root), + } + } + + private requireReservation() { + if (this.reservedName === undefined) + throw new Error('Call fixture.reserve(name) before accessing project files or running a command') + } + + private requireSandboxPath(path: string) { + const offset = relative(this.root, resolve(path)) + if (isAbsolute(offset) || offset === '..' || offset.startsWith(`..${sep}`)) + throw new Error(`Fixture path escapes its temporary sandbox: ${path}`) + } +} + +export const test = baseTest.extend<{fixture: CommandFixture}>({ + fixture: async ({task}, use) => { + const root = await realpath(await mkdtemp(join(tmpdir(), `app-info-${task.id}-`))) + const fixture = new CommandFixture(root) + try { + await fixture.initialize() + await use(fixture) + } finally { + await fixture.dispose() + } + }, +}) diff --git a/test/support/loopback.ts b/test/support/loopback.ts new file mode 100644 index 00000000000..30911f4ce9c --- /dev/null +++ b/test/support/loopback.ts @@ -0,0 +1,25 @@ +import {createServer} from 'node:http' +import type {IncomingMessage, ServerResponse} from 'node:http' + +export async function loopbackServer(handler: (request: IncomingMessage, response: ServerResponse) => void) { + const paths: string[] = [] + const server = createServer((request, response) => { + paths.push(request.url ?? '/') + handler(request, response) + }) + await new Promise((resolve, reject) => { + server.once('error', reject) + server.listen(0, '127.0.0.1', resolve) + }) + const address = server.address() + if (!address || typeof address === 'string') throw new Error('Expected a loopback TCP listener') + return { + url: `http://127.0.0.1:${address.port}`, + port: address.port, + paths, + async close() { + server.closeAllConnections() + await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))) + }, + } +} diff --git a/test/support/observations.ts b/test/support/observations.ts new file mode 100644 index 00000000000..8cb72ecd357 --- /dev/null +++ b/test/support/observations.ts @@ -0,0 +1,35 @@ +import {stripVTControlCharacters} from 'node:util' +import type {CommandFixture} from './fixture.js' + +/** Keep report content and paragraphs, without terminal chrome or incidental padding. */ +export function normalizeText(output: string, fixture: CommandFixture, files: string[] = []): string { + let normalized = stripVTControlCharacters(output).replaceAll('│', '') + // Ink wraps long absolute paths. Match their exact characters across wrapping, + // rather than hiding the entire path row (which would hide a wrong path too). + for (const path of [...files.map((file) => fixture.path(file)), fixture.projectPath, fixture.root]) { + const pattern = [...path].map((character) => character.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('\\s*') + normalized = normalized.replace(new RegExp(pattern, 'g'), path.replace(fixture.root, '')) + } + return normalized + .replaceAll(process.version, '') + .replaceAll(`${process.platform}-${process.arch}`, '') + .split('\n') + .map((line) => line.trim().replace(/ {2,}/g, ' ')) + .filter((line) => !/^[╭╰]─.*[╮╯]$/.test(line)) + .join('\n') + .replace(/\n{3,}/g, '\n\n') + .trim() +} + +export function changedPaths(before: Record, after: Record): string[] { + return [...new Set([...Object.keys(before), ...Object.keys(after)])] + .filter((path) => before[path] !== after[path]) + .sort() +} + +export function storeRelativePath(fixture: CommandFixture, store: 'app' | 'cli-kit'): string { + return fixture + .storePath(store) + .slice(fixture.root.length + 1) + .replaceAll('\\', '/') +} diff --git a/test/support/preload.ts b/test/support/preload.ts new file mode 100644 index 00000000000..bcb5545a712 --- /dev/null +++ b/test/support/preload.ts @@ -0,0 +1,146 @@ +import {http, HttpResponse, passthrough} from 'msw' +import {setupServer} from 'msw/node' +// Guard the native boundary, including calls that bypass CLI-kit's process helper. +// eslint-disable-next-line no-restricted-imports +import childProcess from 'node:child_process' +import {appendFileSync, readFileSync} from 'node:fs' +import {syncBuiltinESMExports} from 'node:module' +import net from 'node:net' +import {isDeepStrictEqual} from 'node:util' +// eslint-disable-next-line no-restricted-imports +import {basename} from 'node:path' +import type {CommandEvent, CommandState} from './protocol.js' + +const state = JSON.parse(readFileSync(process.env.CLI_TEST_STATE!, 'utf8')) as CommandState +const tracePath = process.env.CLI_TEST_TRACE! +const counts = new Map() +let requests = 0 +const startedAt = performance.now() +const subprocessCounts = new Map() +const nativeSpawn = childProcess.spawn +const nativeSpawnSync = childProcess.spawnSync +const nativeConnect = net.Socket.prototype.connect + +function record(event: CommandEvent) { + // Synchronous tracing survives process.exit(2), crashes, and swallowed errors. + appendFileSync(tracePath, `${JSON.stringify({...event, elapsedMs: performance.now() - startedAt})}\n`) +} + +function forbidden(message: string): never { + record({type: 'violation', message}) + throw new Error(message) +} + +const currentTime = () => state.now + (state.clock === 'advancing' ? Math.trunc(performance.now() - startedAt) : 0) +const NativeDate = Date +// Keep Date construction and Date.now consistent, without replacing real timers. +globalThis.Date = new Proxy(NativeDate, { + construct(target, args, newTarget) { + return Reflect.construct(target, args.length ? args : [currentTime()], newTarget) + }, + apply() { + return new NativeDate(currentTime()).toString() + }, + get(target, property) { + return property === 'now' ? currentTime : Reflect.get(target, property) + }, +}) + +const server = setupServer( + http.all('*', async ({request}) => { + // Ink loads its bundled Yoga WASM through a data URL; that is not network I/O. + if (request.url.startsWith('data:')) return passthrough() + const body = await request.text() + let payload: {query?: string; variables?: Record} = {} + try { + payload = JSON.parse(body || '{}') + } catch (error) { + if (!(error instanceof SyntaxError)) throw error + // OAuth and other non-JSON requests still participate in URL/method matching. + } + const operation = payload.query?.match(/\b(?:query|mutation)\s+(\w+)/)?.[1] + const headers = Object.fromEntries(request.headers) + record({ + type: 'request', + url: request.url, + method: request.method, + operation, + variables: payload.variables, + headers, + body, + }) + requests += 1 + if (requests > state.requestLimit) { + record({type: 'violation', message: 'Request ceiling exceeded'}) + process.exit(97) + } + const index = state.requests.findIndex( + (fixture) => + fixture.method === request.method && + fixture.url === request.url && + (fixture.operation === undefined || fixture.operation === operation) && + (fixture.variables === undefined || isDeepStrictEqual(fixture.variables, payload.variables ?? {})) && + Object.entries(fixture.form ?? {}).every(([key, value]) => new URLSearchParams(body).get(key) === value) && + Object.entries(fixture.headers ?? {}).every(([key, value]) => request.headers.get(key) === value), + ) + if (index === -1) return forbidden(`Unregistered request: ${request.method} ${request.url} ${operation ?? ''}`) + if (state.requests[index]!.passthrough) return passthrough() + const used = counts.get(index) ?? 0 + const fixture = state.requests[index]! + const response = fixture.responses[used] ?? (fixture.repeatLastResponse ? fixture.responses.at(-1) : undefined) + if (!response) return forbidden(`Response sequence exhausted: ${operation ?? request.url}`) + counts.set(index, used + 1) + if (response.delayMs) await new Promise((resolve) => setTimeout(resolve, response.delayMs)) + if (response.disconnect) return HttpResponse.error() + const options = {status: response.status ?? 200, headers: response.headers} + return response.text === undefined + ? HttpResponse.json(response.body as Parameters[0], options) + : new HttpResponse(response.text, options) + }), +) +server.listen({onUnhandledRequest: 'error'}) + +// MSW covers Node HTTP(S) and native fetch. This second barrier prevents a missed +// transport (including an SDK) from opening a real socket. +net.Socket.prototype.connect = function (...args: unknown[]) { + const first = Array.isArray(args[0]) ? args[0][0] : args[0] + const options = first as {host?: string; port?: string | number} | undefined + if ( + options && + ['127.0.0.1', '::1'].includes(options.host ?? '') && + state.loopbackPorts.includes(Number(options.port)) + ) { + return Reflect.apply(nativeConnect, this, args) + } + return forbidden('Unintercepted socket connection') +} + +for (const name of ['spawn', 'spawnSync', 'exec', 'execSync', 'execFile', 'execFileSync', 'fork'] as const) { + Object.defineProperty(childProcess, name, { + configurable: true, + value: (command: string, args: string[] = [], options: childProcess.SpawnOptions = {}) => { + record({type: 'spawn', command, args, message: name}) + const index = state.subprocesses.findIndex( + (fixture) => + (fixture.command === command || fixture.command === basename(command)) && + (fixture.args === undefined || isDeepStrictEqual(fixture.args, args)), + ) + const fixture = state.subprocesses[index] + const used = subprocessCounts.get(index) ?? 0 + if (!fixture || used >= (fixture.maxCalls ?? 1) || (name !== 'spawn' && name !== 'spawnSync')) { + return forbidden(`Unexpected command-owned subprocess: ${name} ${command}`) + } + subprocessCounts.set(index, used + 1) + const stubArguments = [process.env.CLI_TEST_SUBPROCESS!, JSON.stringify(fixture)] + // Never execute the requested browser/package manager, even for allowed calls. + if (name === 'spawnSync') return nativeSpawnSync(process.execPath, stubArguments, options) + const child = nativeSpawn(process.execPath, stubArguments, {...options, detached: false}) + // Join even a normally detached opener so no fixture child outlives cleanup. + child.unref = () => child + return child + }, + }) +} +syncBuiltinESMExports() +record({type: 'ready', pid: process.pid}) +process.on('exit', () => record({type: 'exit', pid: process.pid})) diff --git a/test/support/process.ts b/test/support/process.ts new file mode 100644 index 00000000000..7a408a50d20 --- /dev/null +++ b/test/support/process.ts @@ -0,0 +1,166 @@ +// The harness must supervise native processes without importing CLI runtime state. +// eslint-disable-next-line no-restricted-imports +import {spawn} from 'node:child_process' +import {readFileSync} from 'node:fs' +import {stripVTControlCharacters} from 'node:util' +import type {CommandEvent} from './protocol.js' + +export interface TerminalOptions { + replies: {waitFor: string; input: string | string[]}[] +} + +interface ExecutionOptions { + env: NodeJS.ProcessEnv + cwd: string + argv: string[] + timeoutMs: number + stdin?: string + terminal?: TerminalOptions + terminateAfterStdout?: (stdout: string) => boolean +} + +interface ExecutionResult { + stdout: string + stderr: string + terminalOutput: string + answeredPrompts: string[] + exitCode: number | null + signal: NodeJS.Signals | null + timedOut: boolean + terminatedAfterStdout: boolean +} + +function quoteArgument(value: string) { + return `'${value.replaceAll("'", "'\\''")}'` +} + +function terminalArguments(args: string[]) { + if (process.platform === 'darwin') { + return ['--noprofile', '--norc', '-c', 'exec /usr/bin/script -q /dev/null bash "$@" < <(cat)', 'pty-test', ...args] + } + return [ + '--noprofile', + '--norc', + '-c', + 'exec script -qefc "$1" /dev/null < <(cat)', + 'pty-test', + ['bash', ...args].map(quoteArgument).join(' '), + ] +} + +function terminateCommand(env: NodeJS.ProcessEnv) { + // forkpty creates a separate process group. Kill the CLI recorded by our own + // preload as well as script, rather than leaving a polling process orphaned. + const events: CommandEvent[] = readFileSync(env.CLI_TEST_TRACE!, 'utf8') + .split('\n') + .filter(Boolean) + .map((line) => JSON.parse(line)) + for (const event of events.filter((event) => event.type === 'ready')) { + if (!event.pid || events.some((entry) => entry.type === 'exit' && entry.pid === event.pid)) continue + try { + process.kill(event.pid, 'SIGKILL') + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== 'ESRCH') throw error + } + } +} + +export async function runInBash(options: ExecutionOptions): Promise { + const args = ['--noprofile', '--norc', '-c', 'exec shopify "$@"', 'command-test', ...options.argv] + const terminal = options.terminal + // On macOS, script rejects libuv's socket-backed stdin. A real pipe from cat + // supplies its input; the outer bash exec keeps a single supervised process. + const launchArgs = terminal ? terminalArguments(args) : args + // Native process APIs are deliberate: the CLI's module graph belongs in the child. + const child = spawn('bash', launchArgs, {cwd: options.cwd, env: options.env, stdio: ['pipe', 'pipe', 'pipe']}) + const result: ExecutionResult = { + stdout: '', + stderr: '', + terminalOutput: '', + answeredPrompts: [], + exitCode: null, + signal: null, + timedOut: false, + terminatedAfterStdout: false, + } + let consumed = 0 + let searchFrom = 0 + let outputTerminationTimer: ReturnType | undefined + const inputTimers = new Set>() + const timer = setTimeout(() => { + result.timedOut = true + terminateCommand(options.env) + child.kill('SIGKILL') + }, options.timeoutMs) + try { + child.stdout.on('data', (chunk: Buffer) => { + const data = chunk.toString() + if (!terminal) { + result.stdout += data + if (!outputTerminationTimer && options.terminateAfterStdout?.(result.stdout)) { + // Give the command a chance to exit naturally. If it remains alive, + // terminate it after the caller has observed complete output. + outputTerminationTimer = setTimeout(() => { + result.terminatedAfterStdout = true + terminateCommand(options.env) + child.kill('SIGKILL') + }, 100) + } + return + } + // PTYs intentionally merge stdout/stderr. Never label this stream as JSON stdout. + result.terminalOutput += data + const next = terminal.replies[consumed] + const visible = stripVTControlCharacters(result.terminalOutput) + if (next && visible.slice(searchFrom).includes(next.waitFor)) { + consumed += 1 + searchFrom = visible.length + result.answeredPrompts.push(next.waitFor) + // Let Ink finish attaching raw-input effects. Keep text and Enter in + // separate packets so paste handling cannot consume Enter as text. + const chunks = typeof next.input === 'string' ? [next.input] : next.input + chunks.forEach((input, index) => { + const pending = setTimeout( + () => { + inputTimers.delete(pending) + child.stdin.write(input) + }, + 50 * (index + 1), + ) + inputTimers.add(pending) + }) + } + }) + child.stderr.on('data', (chunk: Buffer) => { + result.stderr += chunk.toString() + }) + child.stdin.on('error', () => { + /* Parser errors or Ctrl-C can close stdin early. */ + }) + if (!terminal) child.stdin.end(options.stdin ?? '') + await new Promise((resolve, reject) => { + child.once('error', reject) + child.once('exit', () => child.stdin.end()) + child.once('close', (code, signal) => { + result.exitCode = code + result.signal = signal + resolve() + }) + }) + if (!result.timedOut && terminal && consumed !== terminal.replies.length) { + throw new Error( + `Unanswered prompt: ${terminal.replies[consumed]?.waitFor}\n${result.stderr}${result.terminalOutput}`, + ) + } + return result + } finally { + clearTimeout(timer) + if (outputTerminationTimer) clearTimeout(outputTerminationTimer) + for (const pending of inputTimers) clearTimeout(pending) + child.stdin.destroy() + if (child.exitCode === null && child.signalCode === null) { + terminateCommand(options.env) + child.kill('SIGKILL') + } + } +} diff --git a/test/support/protocol.ts b/test/support/protocol.ts new file mode 100644 index 00000000000..40bfaa5af55 --- /dev/null +++ b/test/support/protocol.ts @@ -0,0 +1,53 @@ +export interface ResponseFixture { + status?: number + headers?: Record + body?: unknown + text?: string + delayMs?: number + disconnect?: boolean +} + +export interface RequestFixture { + method: string + url: string + operation?: string + variables?: Record + form?: Record + headers?: Record + responses: ResponseFixture[] + repeatLastResponse?: boolean + passthrough?: boolean +} + +export interface SubprocessFixture { + command: string + args?: string[] + stdout?: string + stderr?: string + exitCode?: number + maxCalls?: number +} + +export interface CommandState { + now: number + requests: RequestFixture[] + requestLimit: number + clock: 'frozen' | 'advancing' + subprocesses: SubprocessFixture[] + loopbackPorts: number[] +} + +export interface CommandEvent { + type: 'request' | 'violation' | 'spawn' | 'ready' | 'exit' + pid?: number + message?: string + elapsedMs?: number + command?: string + args?: string[] + method?: string + url?: string + operation?: string + variables?: Record + headers?: Record + body?: string +} diff --git a/test/support/scenarios.ts b/test/support/scenarios.ts new file mode 100644 index 00000000000..a01e3e8708b --- /dev/null +++ b/test/support/scenarios.ts @@ -0,0 +1,225 @@ +import { + appAudience, + appManagementUrl, + businessAudience, + businessPlatformUrl, + clientId, + specification, +} from './fixture.js' +import type {CommandFixture} from './fixture.js' +import type {ResponseFixture} from './protocol.js' + +export function mockUiSpecification(fixture: CommandFixture) { + fixture.remote.specifications.push(specification('ui_extension', 'extension')) +} + +export function mockLocalizedSpecification(fixture: CommandFixture) { + fixture.remote.specifications.push( + specification('fixture_configuration', 'configuration', { + type: 'object', + properties: { + fixture_configuration: {type: 'object', properties: {enabled: {type: 'boolean'}}}, + localization: {type: 'object'}, + }, + }), + ) +} + +export const tokenUrl = 'https://accounts.shopify.com/oauth/token' +export const authorizationUrl = 'https://accounts.shopify.com/oauth/device_authorization' +export const errorReportingUrl = 'https://error-analytics-production.shopifysvc.com/' +export const browserUrl = 'https://accounts.example.test/device/fixture' +export function mockErrorReport(fixture: CommandFixture) { + fixture.mockNetwork({method: 'POST', url: errorReportingUrl, responses: [{body: {}}]}) +} +function mockUserTokenExchanges( + fixture: CommandFixture, + tokens = {app: 'synthetic-app-token', business: 'synthetic-business-token'}, +) { + fixture.mockTokenExchange(appAudience, tokens.app) + fixture.mockTokenExchange(businessAudience, tokens.business) + fixture.mockTokenExchange('ee139b3d-5861-4d45-b387-1bc3ada7811c', 'synthetic-storefront-token') + fixture.mockTokenExchange( + '271e16d403dfa18082ffb3d197bd2b5f4479c3fc32736d69296829cbb28d41a6', + 'synthetic-partners-token', + ) +} +export function mockRefreshSession( + fixture: CommandFixture, + tokens = {app: 'synthetic-app-token', business: 'synthetic-business-token'}, +) { + fixture.mockNetwork({ + method: 'POST', + url: tokenUrl, + form: {grant_type: 'refresh_token'}, + responses: [ + { + body: { + access_token: 'synthetic-refreshed-identity', + refresh_token: 'synthetic-refreshed-refresh', + expires_in: 3600, + scope: fixture.user.identity.scopes.join(' '), + }, + }, + ], + }) + mockUserTokenExchanges(fixture, tokens) +} +export async function givenFullAuthentication(fixture: CommandFixture, polls: ResponseFixture[] = []) { + fixture.configure({ + environment: { + CODESPACES: '1', + }, + }) + await fixture.seedState({ + authentication: { + serializedSessions: '', + }, + }) + fixture.mockNetwork({ + method: 'POST', + url: authorizationUrl, + responses: [ + { + body: { + device_code: 'fixture-device', + user_code: 'FIXTURE', + verification_uri: browserUrl, + verification_uri_complete: browserUrl, + interval: 0.01, + expires_in: 60, + }, + }, + ], + }) + const token = `${Buffer.from('{"alg":"none"}').toString('base64url')}.${Buffer.from( + '{"sub":"synthetic-user"}', + ).toString('base64url')}.` + fixture.mockNetwork({ + method: 'POST', + url: tokenUrl, + form: {grant_type: 'urn:ietf:params:oauth:grant-type:device_code'}, + responses: [ + ...polls, + { + body: { + access_token: 'synthetic-new-identity', + refresh_token: 'synthetic-new-refresh', + expires_in: 3600, + scope: fixture.user.identity.scopes.join(' '), + id_token: token, + }, + }, + ], + }) + mockUserTokenExchanges(fixture) + fixture.mockNetwork({ + method: 'POST', + url: businessPlatformUrl, + operation: 'UserEmail', + responses: [{body: {data: {currentUserAccount: {email: 'login@example.test'}}}}], + }) +} +export function mockRemoteConfiguration(fixture: CommandFixture) { + for (const [identifier, config] of [ + ['branding', {name: 'Remote fixture'}], + ['app_home', {app_url: 'https://remote.example.test', embedded: true}], + ['app_access', {scopes: 'read_products', redirect_url_allowlist: ['https://remote.example.test/callback']}], + ] as const) { + fixture.remote.app.activeRelease.version.appModules.push({ + uuid: `fixture-${identifier}`, + userIdentifier: identifier, + handle: identifier, + config, + specification: { + identifier, + externalIdentifier: identifier, + name: identifier, + experience: 'configuration', + managementExperience: 'cli', + }, + }) + } + for (const operation of ['ActiveAppReleaseFromApiKey', 'fetchSpecifications']) { + const request = fixture.networkMocks.find((entry) => entry.operation === operation)! + request.responses.push(structuredClone(request.responses[0]!)) + } +} +export function givenInteractiveLink( + fixture: CommandFixture, + options: { + create?: boolean + organizations?: number + } = {}, +) { + mockRemoteConfiguration(fixture) + fixture.networkMocks.find((request) => request.operation === 'UserInfo')!.repeatLastResponse = true + fixture.mockNetwork({ + method: 'POST', + url: businessPlatformUrl, + operation: 'ListOrganizations', + responses: [ + { + body: { + data: { + currentUserAccount: { + uuid: 'synthetic-user', + organizationsWithAccessToDestination: { + nodes: Array.from({length: options.organizations ?? 1}, (_, index) => ({ + id: Buffer.from(`gid://organization/Organization/${123 + index}`).toString('base64'), + name: `Fixture organization ${index + 1}`, + })), + }, + }, + }, + }, + }, + ], + }) + fixture.mockNetwork({ + method: 'POST', + url: appManagementUrl, + operation: 'listApps', + responses: [ + { + body: { + data: { + appsConnection: { + edges: options.create + ? [] + : [{node: {id: fixture.remote.app.id, key: clientId, activeRelease: fixture.remote.app.activeRelease}}], + pageInfo: {hasNextPage: false}, + }, + }, + }, + }, + ], + }) + if (options.create) { + fixture.mockNetwork({ + method: 'POST', + url: 'https://app.shopify.com/webhooks/unstable/organizations/123/graphql.json', + operation: 'publicApiVersions', + responses: [ + {body: {data: {publicApiVersions: [{handle: '2025-07'}, {handle: 'unstable'}, {handle: '2025-10'}]}}}, + ], + }) + fixture.mockNetwork({ + method: 'POST', + url: appManagementUrl, + operation: 'CreateApp', + responses: [ + { + body: { + data: { + appCreate: { + app: {id: fixture.remote.app.id, key: clientId, activeRoot: fixture.remote.app.activeRoot}, + userErrors: [], + }, + }, + }, + }, + ], + }) + } +} diff --git a/test/support/state.ts b/test/support/state.ts new file mode 100644 index 00000000000..2d5d6b33420 --- /dev/null +++ b/test/support/state.ts @@ -0,0 +1,74 @@ +import {mkdir, readFile, writeFile} from 'node:fs/promises' +// Keep storage setup independent of CLI singletons in the worker. +// eslint-disable-next-line no-restricted-imports +import {dirname, sep} from 'node:path' + +export interface StoredState { + authentication?: { + /** Decoded sessions, including their identity-host and user-ID keys. */ + sessions?: unknown + /** Escape hatch for empty or intentionally malformed serialized session data. */ + serializedSessions?: string + /** Omit to preserve selection; an explicit undefined removes it. */ + currentSessionId?: string + } + appPreferences?: Record + cliPreferences?: {autoUpgradeEnabled?: boolean} + /** Replaces the cache map. An empty object deliberately clears all entries. */ + caches?: Record +} + +interface StorePaths { + project: string + app: string + cliKit: string +} + +async function readStore(path: string): Promise> { + try { + return JSON.parse(await readFile(path, 'utf8')) + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error + return {} + } +} + +async function writeStore(path: string, value: Record) { + await mkdir(dirname(path), {recursive: true}) + await writeFile(path, JSON.stringify(value)) +} + +export async function seedStoredState(paths: StorePaths, state: StoredState) { + if (state.authentication || state.cliPreferences || state.caches !== undefined) { + const store = await readStore(paths.cliKit) + const authentication = state.authentication + if (authentication) { + if (Object.hasOwn(authentication, 'sessions') && Object.hasOwn(authentication, 'serializedSessions')) { + throw new Error('Supply decoded sessions or serializedSessions, not both') + } + if (Object.hasOwn(authentication, 'sessions')) store.sessionStore = JSON.stringify(authentication.sessions) + if (Object.hasOwn(authentication, 'serializedSessions')) store.sessionStore = authentication.serializedSessions + if (Object.hasOwn(authentication, 'currentSessionId')) store.currentSessionId = authentication.currentSessionId + } + Object.assign(store, state.cliPreferences) + if (state.caches !== undefined) store.cache = state.caches + await writeStore(paths.cliKit, store) + } + if (state.appPreferences) { + const store = await readStore(paths.app) + // Match conf's dot-notation lookup, including dots in project paths. + const keys = paths.project.split(sep).join('/').split('.') + let parent = store + for (const key of keys.slice(0, -1)) { + const existing = parent[key] + const child = + existing && typeof existing === 'object' && !Array.isArray(existing) + ? (existing as Record) + : {} + parent[key] = child + parent = child + } + parent[keys.at(-1)!] = {directory: paths.project, ...state.appPreferences} + await writeStore(paths.app, store) + } +} diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 00000000000..6e697c14e61 --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../configurations/tsconfig.json", + "compilerOptions": { + "composite": false, + "declaration": false, + "types": ["node", "vitest/importMeta"], + "paths": { + "msw": ["../packages/cli-kit/node_modules/msw/lib/core/index.d.ts"], + "msw/node": ["../packages/cli-kit/node_modules/msw/lib/node/index.d.ts"] + } + }, + "include": ["./**/*.ts"], + "exclude": ["./fixtures/**", "./node_modules/**"] +} diff --git a/test/vite.config.ts b/test/vite.config.ts new file mode 100644 index 00000000000..6f4564caf87 --- /dev/null +++ b/test/vite.config.ts @@ -0,0 +1,17 @@ +import {defineConfig} from 'vitest/config' +import {availableParallelism} from 'node:os' +import {fileURLToPath} from 'node:url' + +export default defineConfig({ + root: fileURLToPath(new URL('.', import.meta.url)), + test: { + name: 'commands', + include: ['*.test.ts'], + globalSetup: ['./support/build.ts'], + // Cases own their CLI processes and sandboxes; avoid unbounded process fan-out. + maxConcurrency: availableParallelism(), + testTimeout: 20000, + hookTimeout: 30000, + pool: 'forks', + }, +}) diff --git a/tsconfig.json b/tsconfig.json index 3a4094dc89e..0282cdf2852 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "./configurations/tsconfig.json", - "include": ["configurations/**/*"], + "include": ["configurations/**/*", "vite.config.ts"], "references": [ {"path": "./packages/cli"}, {"path": "./packages/app"}, diff --git a/vite.config.ts b/vite.config.ts index 14b1bd99528..fa9c2f95ee0 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -9,6 +9,7 @@ export default defineConfig({ provider: 'istanbul', }, projects: [ + 'test/vite.config.ts', 'packages/app/vite.config.ts', 'packages/cli/vite.config.ts', 'packages/cli-kit/vite.config.ts',