diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index ede9dc089..eac8e669b 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -76,6 +76,29 @@ React Native sample apps can be run against local in-repo SDK sources with `dev rn ios --local` or `dev rn android --local`. The Web sample accepts a checkout URL directly and does not use the shared storefront credential files. +### Test coverage + +CI requires at least **85% statements, branches, functions, and lines** for +each of the Web, React Native JavaScript, and TypeScript protocol packages. +The thresholds apply to each package as a whole, including untested source +files, rather than to individual files. Sample apps, test helpers, and type +declarations do not contribute to these thresholds; the protocol also excludes +generated models and its barrel export. + +Run the same coverage checks locally from the repository root: + +```bash +pnpm --dir platforms/web test +pnpm --dir platforms/react-native test --coverage --testPathPatterns="modules/@shopify/checkout-kit-react-native/tests" +pnpm --dir protocol test --coverage +``` + +A failing threshold fails the test command and its CI job. Add tests for the +uncovered behavior instead of lowering the threshold. The thresholds live in +`platforms/web/vite.config.ts`, `platforms/react-native/jest.config.js`, and +`protocol/vitest.config.ts`. Swift, Android, and standalone telemetry tests do +not yet enforce coverage thresholds. + ### Testing PR builds with Tophat [Tophat](https://github.com/Shopify/tophat) is a macOS menu-bar app that diff --git a/platforms/react-native/jest.config.js b/platforms/react-native/jest.config.js index 43d649fa8..f1fd75484 100644 --- a/platforms/react-native/jest.config.js +++ b/platforms/react-native/jest.config.js @@ -3,6 +3,18 @@ module.exports = { modulePathIgnorePatterns: ['modules/@shopify/checkout-kit-react-native/lib'], modulePaths: ['/node_modules', '/sample/node_modules'], setupFiles: ['/jest.setup.ts'], + collectCoverageFrom: [ + 'modules/@shopify/checkout-kit-react-native/src/**/*.{ts,tsx}', + '!**/*.d.ts', + ], + coverageThreshold: { + global: { + statements: 85, + branches: 85, + functions: 85, + lines: 85, + }, + }, transform: { '\\.[jt]sx?$': 'babel-jest', }, diff --git a/platforms/web/src/checkout.test.ts b/platforms/web/src/checkout.test.ts index fe905896e..c859b7cd4 100644 --- a/platforms/web/src/checkout.test.ts +++ b/platforms/web/src/checkout.test.ts @@ -99,6 +99,40 @@ describe("", () => { }); }); + describe("allowedOrigins", () => { + it("reflects an array of origins to the attribute and back", () => { + const checkout = renderCheckout(); + const origins = ["https://other.example.com", "https://*.example.org"]; + + checkout.allowedOrigins = origins; + + expect(checkout.getAttribute("allowed-origins")).toBe(origins.join(" ")); + expect(checkout.allowedOrigins).toEqual(origins); + }); + + it("parses comma- and whitespace-separated strings without empty entries", () => { + const checkout = renderCheckout(); + const origins = " https://other.example.com, https://*.example.org "; + + checkout.allowedOrigins = origins; + + expect(checkout.getAttribute("allowed-origins")).toBe(origins); + expect(checkout.allowedOrigins).toEqual([ + "https://other.example.com", + "https://*.example.org", + ]); + }); + + it("removes the attribute when assigned undefined", () => { + const checkout = renderCheckout({ "allowed-origins": "https://other.example.com" }); + + checkout.allowedOrigins = undefined; + + expect(checkout.hasAttribute("allowed-origins")).toBe(false); + expect(checkout.allowedOrigins).toEqual([]); + }); + }); + describe("logLevel", () => { it("defaults to 'error' when the attribute is absent", () => { const checkout = renderCheckout(); diff --git a/platforms/web/vite.config.ts b/platforms/web/vite.config.ts index b50f07eb0..6885137ed 100644 --- a/platforms/web/vite.config.ts +++ b/platforms/web/vite.config.ts @@ -67,7 +67,13 @@ export default defineConfig({ provider: 'v8', reporter: ['text', 'json-summary', 'html', 'lcov'], include: ['src/**/*.ts'], - exclude: ['src/**/*.test.ts'], + exclude: ['sample/**', 'src/**/*.test.ts', 'src/**/*.test-helpers.ts', 'src/**/*.d.ts'], + thresholds: { + statements: 85, + branches: 85, + functions: 85, + lines: 85, + }, }, }, }); diff --git a/protocol/vitest.config.ts b/protocol/vitest.config.ts index f0b17778d..b5ac796f0 100644 --- a/protocol/vitest.config.ts +++ b/protocol/vitest.config.ts @@ -13,6 +13,12 @@ export default defineConfig({ 'languages/typescript/src/generated/**', 'languages/typescript/src/index.ts', ], + thresholds: { + statements: 85, + branches: 85, + functions: 85, + lines: 85, + }, }, }, });