@12-apps/payments-e2e 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +112 -0
  2. package/dist/globs.d.ts +43 -0
  3. package/dist/globs.d.ts.map +1 -0
  4. package/dist/globs.js +70 -0
  5. package/dist/globs.js.map +1 -0
  6. package/dist/helpers/checkout.d.ts +32 -0
  7. package/dist/helpers/checkout.d.ts.map +1 -0
  8. package/dist/helpers/checkout.js +47 -0
  9. package/dist/helpers/checkout.js.map +1 -0
  10. package/dist/index.d.ts +42 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +42 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/steps/compra.steps.d.ts +2 -0
  15. package/dist/steps/compra.steps.d.ts.map +1 -0
  16. package/dist/steps/compra.steps.js +193 -0
  17. package/dist/steps/compra.steps.js.map +1 -0
  18. package/dist/steps/fixtures.d.ts +18 -0
  19. package/dist/steps/fixtures.d.ts.map +1 -0
  20. package/dist/steps/fixtures.js +19 -0
  21. package/dist/steps/fixtures.js.map +1 -0
  22. package/dist/steps/loja.steps.d.ts +2 -0
  23. package/dist/steps/loja.steps.d.ts.map +1 -0
  24. package/dist/steps/loja.steps.js +68 -0
  25. package/dist/steps/loja.steps.js.map +1 -0
  26. package/dist/steps/telas.steps.d.ts +2 -0
  27. package/dist/steps/telas.steps.d.ts.map +1 -0
  28. package/dist/steps/telas.steps.js +40 -0
  29. package/dist/steps/telas.steps.js.map +1 -0
  30. package/dist/world.d.ts +139 -0
  31. package/dist/world.d.ts.map +1 -0
  32. package/dist/world.js +25 -0
  33. package/dist/world.js.map +1 -0
  34. package/features/a-compra-sobrevive-ao-primeiro-provedor.feature +28 -0
  35. package/features/a-loja-nao-cobra-online.feature +25 -0
  36. package/features/a-tela-que-o-provedor-pediu.feature +49 -0
  37. package/features/comprando-com-cartao.feature +24 -0
  38. package/features/comprando-com-pix.feature +25 -0
  39. package/features/pagando-na-pagina-do-provedor.feature +31 -0
  40. package/features/quando-o-pagamento-nao-vai.feature +40 -0
  41. package/package.json +42 -0
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The BDD `test` every packaged step binds to (FUT-743, packaged by FUT-561).
3
+ *
4
+ * Extending Playwright's own `test` rather than cucumber's runner is the whole
5
+ * point of playwright-bdd: these scenarios run as ordinary Playwright tests,
6
+ * against the host's own web server, with the same reporters, retries and
7
+ * traces as the specs beside them.
8
+ *
9
+ * There is deliberately NO fixture here beyond Playwright's own. An earlier
10
+ * version carried a `journey` fixture remembering which harness page a scenario
11
+ * had opened, which is exactly the kind of host knowledge that stopped these
12
+ * journeys from shipping with the library. Where a step needs the host, it asks
13
+ * {@link paymentsWorld} — a module-level registry the host installs into — so
14
+ * this `test` stays something a consumer never has to reconstruct.
15
+ */
16
+ export declare const test: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & import("playwright-bdd").BddTestFixtures, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions & import("playwright-bdd").BddWorkerFixtures>;
17
+ export declare const Given: import("playwright-bdd").PlaywrightStyleStepCtor<import("playwright-bdd").PlaywrightStyleStepFn<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & import("playwright-bdd").BddTestFixtures, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions & import("playwright-bdd").BddWorkerFixtures>>, When: import("playwright-bdd").PlaywrightStyleStepCtor<import("playwright-bdd").PlaywrightStyleStepFn<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & import("playwright-bdd").BddTestFixtures, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions & import("playwright-bdd").BddWorkerFixtures>>, Then: import("playwright-bdd").PlaywrightStyleStepCtor<import("playwright-bdd").PlaywrightStyleStepFn<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & import("playwright-bdd").BddTestFixtures, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions & import("playwright-bdd").BddWorkerFixtures>>;
18
+ //# sourceMappingURL=fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../src/steps/fixtures.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,IAAI,qUAAO,CAAC;AAEzB,eAAO,MAAQ,KAAK,oYAAE,IAAI,oYAAE,IAAI,kYAAoB,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { test as base, createBdd } from 'playwright-bdd';
2
+ /**
3
+ * The BDD `test` every packaged step binds to (FUT-743, packaged by FUT-561).
4
+ *
5
+ * Extending Playwright's own `test` rather than cucumber's runner is the whole
6
+ * point of playwright-bdd: these scenarios run as ordinary Playwright tests,
7
+ * against the host's own web server, with the same reporters, retries and
8
+ * traces as the specs beside them.
9
+ *
10
+ * There is deliberately NO fixture here beyond Playwright's own. An earlier
11
+ * version carried a `journey` fixture remembering which harness page a scenario
12
+ * had opened, which is exactly the kind of host knowledge that stopped these
13
+ * journeys from shipping with the library. Where a step needs the host, it asks
14
+ * {@link paymentsWorld} — a module-level registry the host installs into — so
15
+ * this `test` stays something a consumer never has to reconstruct.
16
+ */
17
+ export const test = base;
18
+ export const { Given, When, Then } = createBdd(test);
19
+ //# sourceMappingURL=fixtures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures.js","sourceRoot":"","sources":["../../src/steps/fixtures.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,IAAI,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEzD;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,CAAC;AAEzB,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=loja.steps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loja.steps.d.ts","sourceRoot":"","sources":["../../src/steps/loja.steps.ts"],"names":[],"mappings":""}
@@ -0,0 +1,68 @@
1
+ import { expect } from '@playwright/test';
2
+ import { paymentsWorld } from '../world.js';
3
+ import { Given } from './fixtures.js';
4
+ /**
5
+ * WHICH STORE a scenario is set in (FUT-743, packaged by FUT-561).
6
+ *
7
+ * Every one of these is a fact about the MERCHANT — what it connected, what
8
+ * that provider does, whether it charges online at all — and none of them is a
9
+ * fact about the buyer. They are separate from the buyer's own steps for that
10
+ * reason: the same journey is worth telling in several stores, and a Given that
11
+ * mixed the two would have to be rewritten per store.
12
+ *
13
+ * None of them knows how a store is BUILT. Each names a shape and hands it to
14
+ * the host's `PaymentsWorld.open`, which is the only part that differs between
15
+ * an in-page harness and a real app seeding a tenant. That is what lets these
16
+ * ship with the library instead of being copied into every consumer.
17
+ */
18
+ Given('a compradora abre o checkout da loja de PIX', async ({ page }) => {
19
+ await paymentsWorld().open(page, 'pix-only');
20
+ });
21
+ Given('a compradora abre o checkout da loja de cartão', async ({ page }) => {
22
+ await paymentsWorld().open(page, 'card');
23
+ });
24
+ Given('a compradora abre o checkout de uma loja que aceita os dois', async ({ page }) => {
25
+ await paymentsWorld().open(page, 'both-methods');
26
+ });
27
+ Given('a compradora abre o checkout de uma loja de página externa', async ({ page }) => {
28
+ await paymentsWorld().open(page, 'hosted');
29
+ });
30
+ Given('a loja ainda não recebeu o pagamento', async ({ page }) => {
31
+ await paymentsWorld().open(page, 'awaiting');
32
+ });
33
+ Given('a loja confirma o pagamento na primeira consulta', async ({ page }) => {
34
+ await paymentsWorld().open(page, 'settles');
35
+ });
36
+ Given('o provedor da loja recusa o cartão', async ({ page }) => {
37
+ await paymentsWorld().open(page, 'declined');
38
+ });
39
+ /** Nothing can prove whether the money moved, and the provider answers no probe. */
40
+ Given('o provedor da loja não responde de forma conclusiva', async ({ page }) => {
41
+ await paymentsWorld().open(page, 'unresolved');
42
+ });
43
+ /** Provably nothing left the building — the walk may advance, and finds nobody. */
44
+ Given('o provedor da loja está fora do ar', async ({ page }) => {
45
+ await paymentsWorld().open(page, 'unavailable');
46
+ });
47
+ Given('a loja não tem provedor nenhum conectado', async ({ page }) => {
48
+ await paymentsWorld().open(page, 'no-provider');
49
+ });
50
+ Given('a loja não tem provedor nenhum e oferece chamar o garçom', async ({ page }) => {
51
+ await paymentsWorld().open(page, 'no-provider-remedy');
52
+ });
53
+ /**
54
+ * The chain is live and the config read succeeds. Only the application knows
55
+ * this store has switched online payments off, so only the application can say.
56
+ */
57
+ Given('a loja tem provedor mas desligou os pagamentos online', async ({ page }) => {
58
+ const world = paymentsWorld();
59
+ await world.open(page, 'payments-off');
60
+ await expect(world.wire.paths(page)).toHaveText('GET /api/checkout/config');
61
+ });
62
+ Given('a loja tem dois provedores que geram token no navegador', async ({ page }) => {
63
+ await paymentsWorld().open(page, 'two-mintable');
64
+ });
65
+ Given('a loja tem um provedor de página externa seguido de um que gera token', async ({ page }) => {
66
+ await paymentsWorld().open(page, 'redirect-head');
67
+ });
68
+ //# sourceMappingURL=loja.steps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loja.steps.js","sourceRoot":"","sources":["../../src/steps/loja.steps.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=telas.steps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telas.steps.d.ts","sourceRoot":"","sources":["../../src/steps/telas.steps.ts"],"names":[],"mappings":""}
@@ -0,0 +1,40 @@
1
+ import { expect } from '@playwright/test';
2
+ import { paymentsWorld } from '../world.js';
3
+ import { Given, Then } from './fixtures.js';
4
+ /**
5
+ * WHICH SCREEN THE STORE'S PROVIDER ASKED FOR (FUT-596, packaged by FUT-561).
6
+ *
7
+ * These are facts about the merchant's adapter, so they sit beside
8
+ * `loja.steps.ts`'s Givens rather than in the buyer's own file — the buyer does
9
+ * nothing differently in any of them, which is exactly the claim: she fills the
10
+ * same form and presses the same button, and the pane underneath is whichever
11
+ * the provider declared.
12
+ *
13
+ * The store shapes are declarative, so a host satisfies them however it can:
14
+ * the harness declares a chain in-page, a real app seeds a tenant whose adapter
15
+ * declares the id. Either way the id is published by `GET /checkout/config` and
16
+ * resolved by the checkout, so these fail if the field is dropped anywhere
17
+ * along that path.
18
+ */
19
+ Given('a loja declara que cobra na própria página', async ({ page }) => {
20
+ await paymentsWorld().open(page, 'screen-on-page');
21
+ });
22
+ Given('a loja declara que a compradora termina no provedor', async ({ page }) => {
23
+ await paymentsWorld().open(page, 'screen-handoff');
24
+ });
25
+ /** Stone and Stripe today: the capability default answers for them. */
26
+ Given('a loja não declara tela nenhuma', async ({ page }) => {
27
+ await paymentsWorld().open(page, 'screen-undeclared');
28
+ });
29
+ /**
30
+ * A newer server against an older bundle. The two packages version
31
+ * independently, so this is an ordinary deployment state — and the one where
32
+ * "unknown id ⇒ render nothing" would take the whole checkout down silently.
33
+ */
34
+ Given('a loja declara uma tela que este pacote não conhece', async ({ page }) => {
35
+ await paymentsWorld().open(page, 'screen-unknown');
36
+ });
37
+ Then('ela é avisada de que vai ser levada para o provedor', async ({ page }) => {
38
+ await expect(page.getByTestId('checkout-handoff-pending')).toBeVisible();
39
+ });
40
+ //# sourceMappingURL=telas.steps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telas.steps.js","sourceRoot":"","sources":["../../src/steps/telas.steps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE5C;;;;;;;;;;;;;;GAcG;AAEH,KAAK,CAAC,4CAA4C,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;IACrE,MAAM,aAAa,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;AACrD,CAAC,CAAC,CAAC;AAEH,KAAK,CAAC,qDAAqD,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;IAC9E,MAAM,aAAa,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;AACrD,CAAC,CAAC,CAAC;AAEH,uEAAuE;AACvE,KAAK,CAAC,iCAAiC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;IAC1D,MAAM,aAAa,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;AACxD,CAAC,CAAC,CAAC;AAEH;;;;GAIG;AACH,KAAK,CAAC,qDAAqD,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;IAC9E,MAAM,aAAa,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;AACrD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,qDAAqD,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;IAC7E,MAAM,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,0BAA0B,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;AAC3E,CAAC,CAAC,CAAC"}
@@ -0,0 +1,139 @@
1
+ import type { Page } from '@playwright/test';
2
+ /**
3
+ * The port a HOST implements to run the packaged payment journeys (FUT-561).
4
+ *
5
+ * The journeys themselves are portable: every assertion in them reads a test id
6
+ * the payments package's own components render, so `card-number`,
7
+ * `checkout-method-CARD` and `payment-paid` mean the same thing in any app that
8
+ * mounts the checkout. What is NOT portable is everything around them — how
9
+ * this app routes to a checkout, how a merchant of a given shape gets set up,
10
+ * and where a host records what crossed the wire.
11
+ *
12
+ * That is the whole of this port. A host implements it once, adds one glob to
13
+ * its bdd config, and inherits every scenario the library ships — including the
14
+ * ones added after it integrated. Nothing is copied, so nothing can rot.
15
+ */
16
+ /**
17
+ * The SHAPE of a store a scenario is set in — never a vendor, and never a
18
+ * screen id either.
19
+ *
20
+ * A scenario says what kind of merchant it is talking about; the host decides
21
+ * how to produce one. That indirection is what lets the same journey run
22
+ * against a harness that declares an in-page provider chain and against a real
23
+ * app that seeds a tenant, without either of them editing the feature file.
24
+ *
25
+ * Adding a member here is a breaking change for hosts, which is the correct
26
+ * cost: a new scenario that needs a store nobody can build is a scenario that
27
+ * silently never runs.
28
+ */
29
+ export type PaymentsStore =
30
+ /** One provider, PIX only — the simplest store there is. */
31
+ 'pix-only'
32
+ /** One provider that mints card instruments in the browser. */
33
+ | 'card'
34
+ /** One provider offering both methods. */
35
+ | 'both-methods'
36
+ /** The buyer finishes on the provider's own page. */
37
+ | 'hosted'
38
+ /** A PIX code nobody has paid yet — the state a buyer sits in while deciding. */
39
+ | 'awaiting'
40
+ /** The provider confirms on the first poll. */
41
+ | 'settles'
42
+ /** The issuer refuses. A real decline, not an outage. */
43
+ | 'declined'
44
+ /** Nobody can prove whether the money moved, and no probe answers. */
45
+ | 'unresolved'
46
+ /** Provably nothing left the building — the walk may advance. */
47
+ | 'unavailable'
48
+ /** No provider connected at all. */
49
+ | 'no-provider'
50
+ /** No provider, but the host offers something else to do. */
51
+ | 'no-provider-remedy'
52
+ /** A live chain the APPLICATION has switched online payments off for. */
53
+ | 'payments-off'
54
+ /** Two providers that both mint in the browser. */
55
+ | 'two-mintable'
56
+ /** A hand-off provider at the head, a minting one behind it. */
57
+ | 'redirect-head'
58
+ /** Declares the on-page screen (FUT-596). */
59
+ | 'screen-on-page'
60
+ /** Declares the hand-off screen (FUT-596). */
61
+ | 'screen-handoff'
62
+ /** Declares no screen — the capability default answers. */
63
+ | 'screen-undeclared'
64
+ /** Declares a screen id this bundle has never shipped. */
65
+ | 'screen-unknown';
66
+ /**
67
+ * Facts about the host's own fixtures that the assertions have to name.
68
+ *
69
+ * These exist because a few Thens are about WHAT CROSSED THE WIRE, not about
70
+ * what is on the screen — which provider received the charge, which host the
71
+ * buyer was sent to. Those are the assertions that caught three criticals in
72
+ * FUT-740, so they are worth the coupling; parameterising them here is what
73
+ * keeps the feature files free of one host's invented vendor names.
74
+ */
75
+ export interface PaymentsFixtures {
76
+ /** The first provider of a multi-entry chain. */
77
+ headProvider: string;
78
+ /** The provider behind it. */
79
+ tailProvider: string;
80
+ /** A substring of the hosted checkout URL the buyer is sent to. */
81
+ hostedUrlFragment: string;
82
+ /** A substring of the payable reference the host parks before navigating. */
83
+ payableRef: string;
84
+ /** The CPF the buyer types, as the provider should receive it. */
85
+ taxId: string;
86
+ }
87
+ /**
88
+ * Where a host records what actually crossed the wire.
89
+ *
90
+ * A host that renders no probe can return locators that never resolve — the
91
+ * scenarios asserting on them will fail loudly, which is the honest outcome:
92
+ * those journeys are the ones that only pass because somebody is watching the
93
+ * bytes, and quietly skipping them would report a coverage that is not there.
94
+ */
95
+ export interface PaymentsWireProbe {
96
+ /** Every request path the client made, in order. */
97
+ paths(page: Page): ReturnType<Page['getByTestId']>;
98
+ /** The charge body's own top-level keys. */
99
+ chargeKeys(page: Page): ReturnType<Page['getByTestId']>;
100
+ /** The charge body, verbatim. */
101
+ chargeBody(page: Page): ReturnType<Page['getByTestId']>;
102
+ /** The instrument map, or `(absent)` when none was sent. */
103
+ tokensByProvider(page: Page): ReturnType<Page['getByTestId']>;
104
+ /** Every charge a provider actually received, tagged with who received it. */
105
+ providerCharges(page: Page): ReturnType<Page['getByTestId']>;
106
+ /** How many charges reached a provider. */
107
+ providerChargeCount(page: Page): ReturnType<Page['getByTestId']>;
108
+ /** Where the host was asked to navigate, on a hand-off. */
109
+ navigated(page: Page): ReturnType<Page['getByTestId']>;
110
+ }
111
+ /** Everything a host supplies to run the packaged journeys. */
112
+ export interface PaymentsWorld {
113
+ /** Put the browser on a checkout for a store of this shape. */
114
+ open(page: Page, store: PaymentsStore): Promise<void>;
115
+ /** Raise a hosted payable and show the hand-off interstitial. */
116
+ raiseHostedPayable(page: Page): Promise<void>;
117
+ /** Bring the buyer back from the provider's page, as a real return trip. */
118
+ returnFromProvider(page: Page): Promise<void>;
119
+ /** The terminal status the return leg settles to. */
120
+ hostedReturnStatus(page: Page): ReturnType<Page['getByTestId']>;
121
+ fixtures: PaymentsFixtures;
122
+ wire: PaymentsWireProbe;
123
+ }
124
+ /**
125
+ * Install the host's implementation. Call this from a module inside the host's
126
+ * OWN steps glob — playwright-bdd imports every step file before any scenario
127
+ * runs, so a top-level call there is registered in time, in every worker.
128
+ */
129
+ export declare function definePaymentsWorld(world: PaymentsWorld): void;
130
+ /**
131
+ * The installed world.
132
+ *
133
+ * Throws rather than degrading: a journey that ran against a half-configured
134
+ * world would fail somewhere deep inside a step with a message about a missing
135
+ * element, and the actual cause — a host that forgot to call
136
+ * {@link definePaymentsWorld} — would be several layers away from the error.
137
+ */
138
+ export declare function paymentsWorld(): PaymentsWorld;
139
+ //# sourceMappingURL=world.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"world.d.ts","sourceRoot":"","sources":["../src/world.ts"],"names":[],"mappings":"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"}
package/dist/world.js ADDED
@@ -0,0 +1,25 @@
1
+ let installed = null;
2
+ /**
3
+ * Install the host's implementation. Call this from a module inside the host's
4
+ * OWN steps glob — playwright-bdd imports every step file before any scenario
5
+ * runs, so a top-level call there is registered in time, in every worker.
6
+ */
7
+ export function definePaymentsWorld(world) {
8
+ installed = world;
9
+ }
10
+ /**
11
+ * The installed world.
12
+ *
13
+ * Throws rather than degrading: a journey that ran against a half-configured
14
+ * world would fail somewhere deep inside a step with a message about a missing
15
+ * element, and the actual cause — a host that forgot to call
16
+ * {@link definePaymentsWorld} — would be several layers away from the error.
17
+ */
18
+ export function paymentsWorld() {
19
+ if (!installed) {
20
+ throw new Error('No PaymentsWorld installed. Call definePaymentsWorld(...) from a module ' +
21
+ "inside this app's bdd `steps` glob — see @12-apps/payments-e2e's README.");
22
+ }
23
+ return installed;
24
+ }
25
+ //# sourceMappingURL=world.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"world.js","sourceRoot":"","sources":["../src/world.ts"],"names":[],"mappings":"AAiIA,IAAI,SAAS,GAAyB,IAAI,CAAC;AAE3C;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAoB;IACtD,SAAS,GAAG,KAAK,CAAC;AACpB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,0EAA0E;YACxE,0EAA0E,CAC7E,CAAC;IACJ,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,28 @@
1
+ @journey @cadeia
2
+ Feature: A compra sobrevive ao primeiro provedor falhar
3
+
4
+ A lojista ordenou dois provedores. Do lado de quem compra isso tem que ser
5
+ invisível: o cartão é digitado UMA vez e a compra atravessa o primeiro
6
+ provedor caindo.
7
+
8
+ Só que um token de cartão pertence a quem o gerou, então a caminhada só
9
+ consegue avançar para um provedor para o qual o navegador também gerou um. É
10
+ por isso que a quantidade de instrumentos enviados é uma decisão do checkout,
11
+ e é por isso que estes cenários olham para o que foi enviado e não só para a
12
+ tela.
13
+
14
+
15
+ Scenario: O primeiro provedor cai e o segundo cobra, sem redigitar nada
16
+ Given a loja tem dois provedores que geram token no navegador
17
+ When ela informa o CPF e segue para o pagamento
18
+ And ela paga com um cartão novo
19
+ Then o pagamento é confirmado
20
+ And um instrumento foi gerado para cada provedor da cadeia
21
+ And os dois provedores receberam a cobrança
22
+
23
+ Scenario: O primeiro provedor é uma página externa e mesmo assim o mapa é enviado
24
+ Given a loja tem um provedor de página externa seguido de um que gera token
25
+ When ela informa o CPF e segue para o pagamento
26
+ And ela escolhe pagar com cartão
27
+ And ela paga com um cartão novo
28
+ Then o mapa de instrumentos traz só o provedor que gera token
@@ -0,0 +1,25 @@
1
+ @journey @indisponivel
2
+ Feature: A loja não cobra online
3
+
4
+ Duas coisas diferentes levam à mesma tela, e confundir uma com a outra é um
5
+ defeito de verdade: uma loja que nunca conectou provedor nenhum, e uma loja
6
+ que tem provedor mas desligou os pagamentos online. A primeira o servidor
7
+ sabe; a segunda só a aplicação sabe. Se a decisão passar a ser de um lado só,
8
+ uma das duas lojas volta a oferecer um checkout que não vai honrar.
9
+
10
+
11
+ Scenario: Sem provedor nenhum, nada de pagamento aparece e nada é cobrado
12
+ Given a loja não tem provedor nenhum conectado
13
+ Then ela é avisada de que a loja não cobra online
14
+ And nenhuma forma de pagamento é oferecida
15
+ And nenhum pedido chegou a ser criado
16
+
17
+ Scenario: Sem provedor, mas com alguém para chamar
18
+ Given a loja não tem provedor nenhum e oferece chamar o garçom
19
+ Then ela recebe a opção de chamar o garçom
20
+ And nenhuma forma de pagamento é oferecida
21
+
22
+ Scenario: A loja tem provedor mas desligou o pagamento online
23
+ Given a loja tem provedor mas desligou os pagamentos online
24
+ Then ela é avisada de que a loja não cobra online
25
+ And nenhuma forma de pagamento é oferecida
@@ -0,0 +1,49 @@
1
+ @journey @telas-de-provedor
2
+ Feature: A tela que o provedor pediu
3
+
4
+ Cada provedor tem um jeito de cobrar, e por muito tempo o checkout desenhava
5
+ um só, cheio de condicionais. Agora quem cobra na nossa página e quem cobra na
6
+ página dele pedem telas diferentes — e é o ADAPTADOR que pede, não o checkout
7
+ que adivinha.
8
+
9
+ O id que ele declara nomeia a FORMA do fluxo, nunca a marca. Por isso as lojas
10
+ aqui têm nomes inventados: uma loja "aurora" que declara a mesma forma recebe
11
+ exatamente a mesma tela que um adquirente de verdade receberia.
12
+
13
+ As duas últimas cenas são as que importam de madrugada. Um provedor que não
14
+ declara nada continua vendendo, e um id que este pacote nunca viu — servidor
15
+ mais novo que o navegador, coisa normal em deploy — cai na tela de capacidade
16
+ em vez de deixar a compradora olhando para um painel vazio.
17
+
18
+
19
+ Scenario: Quem cobra na nossa página mostra o formulário aqui mesmo
20
+ Given a loja declara que cobra na própria página
21
+ When ela informa o CPF e segue para o pagamento
22
+ And ela escolhe pagar com cartão
23
+ Then ela vê o formulário de cartão
24
+
25
+ Scenario: Quem cobra na página dele não oferece formulário nenhum
26
+ Given a loja declara que a compradora termina no provedor
27
+ When ela informa o CPF e segue para o pagamento
28
+ And ela escolhe pagar com cartão
29
+ Then nenhum formulário de cartão é mostrado
30
+ And ela é avisada de que vai ser levada para o provedor
31
+
32
+ Scenario: Um provedor que não declara tela nenhuma continua vendendo
33
+ Given a loja não declara tela nenhuma
34
+ When ela informa o CPF e segue para o pagamento
35
+ And ela escolhe pagar com cartão
36
+ Then ela vê o formulário de cartão
37
+
38
+ Scenario: Um id que este pacote não conhece não deixa a tela em branco
39
+ Given a loja declara uma tela que este pacote não conhece
40
+ When ela informa o CPF e segue para o pagamento
41
+ And ela escolhe pagar com cartão
42
+ Then ela vê o formulário de cartão
43
+
44
+ Scenario: A compradora paga normalmente na loja que declara a nossa tela
45
+ Given a loja declara que cobra na própria página
46
+ When ela informa o CPF e segue para o pagamento
47
+ And ela escolhe pagar com cartão
48
+ And ela paga com um cartão novo
49
+ Then o pagamento é confirmado
@@ -0,0 +1,24 @@
1
+ @journey @cartao
2
+ Feature: Comprando com cartão
3
+
4
+ A compradora digita o cartão uma vez. O que acontece com esses dados entre o
5
+ navegador e o provedor é onde os três defeitos críticos da FUT-740 moravam —
6
+ com todos os quinze checks verdes — então cada cenário aqui termina olhando
7
+ para o que realmente atravessou.
8
+
9
+ Background:
10
+ Given a compradora abre o checkout da loja de cartão
11
+
12
+ Scenario: O cartão é cobrado e o CPF chega ao provedor
13
+ When ela informa o CPF e segue para o pagamento
14
+ Then ela vê o formulário de cartão
15
+ When ela paga com um cartão novo
16
+ Then o pagamento é confirmado
17
+ And a cobrança enviada ao provedor carrega o cartão e o CPF dela
18
+ And o corpo da cobrança é o formato plano que o cliente publicado envia
19
+
20
+ Scenario: Uma loja com um provedor só não envia tokensByProvider
21
+ When ela informa o CPF e segue para o pagamento
22
+ And ela paga com um cartão novo
23
+ Then o pagamento é confirmado
24
+ And nenhum tokensByProvider é enviado
@@ -0,0 +1,25 @@
1
+ @journey @pix
2
+ Feature: Comprando com PIX numa loja que só aceita PIX
3
+
4
+ A loja ligou um único provedor e ele só cobra por PIX. Do ponto de vista de
5
+ quem compra, isso não é uma escolha: a tela mostra uma opção só, já marcada, e
6
+ o código aparece sem nenhum toque a mais.
7
+
8
+ O QR não é desenhado pelo checkout — ele vem do payload que o provedor gerou e
9
+ que a loja carregou de volta na própria resposta. Um payload que não sobrevive
10
+ a esse caminho vira uma tela de pagamento sem código nenhum, e é isso que
11
+ estes cenários vigiam.
12
+
13
+
14
+ Scenario: O PIX já vem marcado e o código é o do provedor
15
+ Given a loja ainda não recebeu o pagamento
16
+ When ela informa o CPF e segue para o pagamento
17
+ Then só a opção PIX é oferecida
18
+ And ela vê o QR code do PIX
19
+ And o código copia-e-cola veio do provedor
20
+
21
+ Scenario: O pagamento é confirmado sozinho e a compradora vê o recibo
22
+ Given a loja confirma o pagamento na primeira consulta
23
+ When ela informa o CPF e segue para o pagamento
24
+ Then o pagamento é confirmado
25
+ And o recibo mostra o valor pago
@@ -0,0 +1,31 @@
1
+ @journey @redirecionamento
2
+ Feature: Pagando na página do provedor
3
+
4
+ Quando ninguém na cadeia consegue gerar token no navegador, o cartão é
5
+ digitado na página do próprio provedor. A compradora sai do site da loja e
6
+ volta depois — e essa volta é a parte que costuma quebrar em silêncio: a
7
+ aplicação foi destruída pela navegação, então a única coisa que resta é o que
8
+ foi guardado ANTES de sair.
9
+
10
+ Guardar primeiro e navegar depois é a regra. Invertida, a compradora paga e
11
+ volta para uma tela em branco.
12
+
13
+ Background:
14
+ Given a compradora abre o checkout de uma loja de página externa
15
+
16
+ Scenario: Nenhum formulário de cartão é oferecido, e a saída fica registrada
17
+ When ela informa o CPF e segue para o pagamento
18
+ And ela escolhe pagar com cartão
19
+ Then nenhum formulário de cartão é mostrado
20
+ And a loja registrou para onde ela seria levada
21
+
22
+ Scenario: A tela de transferência guarda o pedido e oferece um link de verdade
23
+ When a loja gera o pagamento hospedado
24
+ Then ela vê a tela de transferência para o provedor
25
+ And existe um link visível para a página do provedor
26
+ And o pedido foi guardado antes da navegação
27
+
28
+ Scenario: Ela volta do provedor e o pagamento aparece confirmado
29
+ When a loja gera o pagamento hospedado
30
+ And ela volta da página do provedor
31
+ Then o pagamento volta confirmado
@@ -0,0 +1,40 @@
1
+ @journey @recusas
2
+ Feature: Quando o pagamento não vai
3
+
4
+ Recusa não é uma coisa só, e tratar as três como se fossem é o que produz o
5
+ dano. O emissor recusar é um problema que a compradora resolve; uma cobrança
6
+ que ninguém consegue confirmar é a única situação em que oferecer "tentar de
7
+ novo" pode custar dinheiro de verdade a ela; e uma cadeia esgotada não tem
8
+ nada a ver com os dados que ela digitou.
9
+
10
+ A loja destes cenários aceita PIX e cartão, então escolher o cartão é um
11
+ passo de verdade — e no último cenário é justamente o que sobra.
12
+
13
+
14
+ Scenario: O emissor recusa e ela pode tentar de novo
15
+ Given o provedor da loja recusa o cartão
16
+ When ela informa o CPF e segue para o pagamento
17
+ And ela escolhe pagar com cartão
18
+ Then ela vê o formulário de cartão
19
+ When ela paga com um cartão recusado
20
+ Then o pagamento falha
21
+ And ela pode tentar novamente
22
+
23
+ Scenario: Ninguém consegue confirmar a cobrança, então não há como pagar de novo
24
+ Given o provedor da loja não responde de forma conclusiva
25
+ When ela informa o CPF e segue para o pagamento
26
+ And ela escolhe pagar com cartão
27
+ Then ela vê o formulário de cartão
28
+ When ela paga com um cartão novo
29
+ Then ela é avisada de que o pagamento está sendo confirmado
30
+ And não existe nenhum botão de pagar na tela
31
+ And não existe nenhum botão de tentar novamente na tela
32
+
33
+ Scenario: A cadeia se esgota e o PIX continua disponível
34
+ Given o provedor da loja está fora do ar
35
+ When ela informa o CPF e segue para o pagamento
36
+ And ela escolhe pagar com cartão
37
+ Then ela vê o formulário de cartão
38
+ When ela paga com um cartão novo
39
+ Then o erro do cartão menciona o outro meio de pagamento
40
+ And a opção PIX continua na tela
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@12-apps/payments-e2e",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "The payments buyer journeys, as Gherkin a host runs against its own app",
6
+ "files": [
7
+ "dist",
8
+ "features",
9
+ "*.md"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "./package.json": "./package.json"
17
+ },
18
+ "scripts": {
19
+ "build": "tsc -p tsconfig.json",
20
+ "clean": "rm -rf node_modules dist",
21
+ "lint": "eslint src --max-warnings 0",
22
+ "check-types": "tsc --noEmit -p tsconfig.json",
23
+ "typecheck": "tsc --noEmit -p tsconfig.json"
24
+ },
25
+ "peerDependencies": {
26
+ "@playwright/test": ">=1.40.0",
27
+ "playwright-bdd": ">=8.0.0"
28
+ },
29
+ "devDependencies": {
30
+ "@12-apps/eslint-config": "^1.16.2",
31
+ "@12-apps/typescript-config": "^1.16.2",
32
+ "@playwright/test": "^1.56.1",
33
+ "playwright-bdd": "^8.4.1",
34
+ "typescript": "5.9.2"
35
+ },
36
+ "publishConfig": {
37
+ "registry": "https://registry.npmjs.org",
38
+ "access": "public"
39
+ },
40
+ "main": "./dist/index.js",
41
+ "types": "./dist/index.d.ts"
42
+ }