@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.
- package/README.md +112 -0
- package/dist/globs.d.ts +43 -0
- package/dist/globs.d.ts.map +1 -0
- package/dist/globs.js +70 -0
- package/dist/globs.js.map +1 -0
- package/dist/helpers/checkout.d.ts +32 -0
- package/dist/helpers/checkout.d.ts.map +1 -0
- package/dist/helpers/checkout.js +47 -0
- package/dist/helpers/checkout.js.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/steps/compra.steps.d.ts +2 -0
- package/dist/steps/compra.steps.d.ts.map +1 -0
- package/dist/steps/compra.steps.js +193 -0
- package/dist/steps/compra.steps.js.map +1 -0
- package/dist/steps/fixtures.d.ts +18 -0
- package/dist/steps/fixtures.d.ts.map +1 -0
- package/dist/steps/fixtures.js +19 -0
- package/dist/steps/fixtures.js.map +1 -0
- package/dist/steps/loja.steps.d.ts +2 -0
- package/dist/steps/loja.steps.d.ts.map +1 -0
- package/dist/steps/loja.steps.js +68 -0
- package/dist/steps/loja.steps.js.map +1 -0
- package/dist/steps/telas.steps.d.ts +2 -0
- package/dist/steps/telas.steps.d.ts.map +1 -0
- package/dist/steps/telas.steps.js +40 -0
- package/dist/steps/telas.steps.js.map +1 -0
- package/dist/world.d.ts +139 -0
- package/dist/world.d.ts.map +1 -0
- package/dist/world.js +25 -0
- package/dist/world.js.map +1 -0
- package/features/a-compra-sobrevive-ao-primeiro-provedor.feature +28 -0
- package/features/a-loja-nao-cobra-online.feature +25 -0
- package/features/a-tela-que-o-provedor-pediu.feature +49 -0
- package/features/comprando-com-cartao.feature +24 -0
- package/features/comprando-com-pix.feature +25 -0
- package/features/pagando-na-pagina-do-provedor.feature +31 -0
- package/features/quando-o-pagamento-nao-vai.feature +40 -0
- 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 @@
|
|
|
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 @@
|
|
|
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"}
|
package/dist/world.d.ts
ADDED
|
@@ -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
|
+
}
|