diff options
| author | Murilo Ijanc <[email protected]> | 2026-07-27 20:52:15 -0300 |
|---|---|---|
| committer | Murilo Ijanc <[email protected]> | 2026-07-27 20:52:15 -0300 |
| commit | 511a0ab33b5b12a4cd8a1d4dca6c5dce50cdb6c1 (patch) | |
| tree | 462412b439743c2d04f1acf24f6557251d3a8265 /test | |
| parent | 535dbda6b48171f8115b455e54790de94d97d42a (diff) | |
| download | woocommerce-0.2.0.tar.gz | |
A CEP that fails at the checkout is almost never a random number. It is
a shopper who knows perfectly well which town they live in and got a
digit wrong, most often by reaching for the -000 that 957 municipalities
never received. The API now names the municipality such a number would
have belonged to, in the body of its own 404, so the message can say
"that would be in Areal/RJ, which has CEPs from 25845-001 to 25849-899"
instead of leaving the shopper at a dead end with nothing to act on.
It stays an error. Nothing from it is written into a checkout field, on
either checkout and in either lookup mode: a CEP that does not exist has
no address, and filling one in from the municipality behind it means a
parcel delivered to the wrong town and a chargeback behind that. The one
module that reads the 404 body hands its callers a finished sentence and
never the data itself, so there is no shape in which it could leak into
a field later. The two CEPs quoted are the lowest and the highest that
really exist there, never a list and never a suggestion, because the
range is sparse enough that a number out of the middle of it would most
likely not exist either.
The widget correctly throws its 404 away without reading it, so the body
is fetched once more, on the miss path only and once per CEP. In proxy
mode PHP passes the municipality through with the 404 and caches it with
the miss, which is what keeps the two modes indistinguishable from the
browser. Anything missing or misshapen falls back to the message the
plugin has always shown.
Diffstat (limited to 'test')
| -rw-r--r-- | test/checkout.test.js | 171 | ||||
| -rw-r--r-- | test/fake-api.js | 44 |
2 files changed, 209 insertions, 6 deletions
diff --git a/test/checkout.test.js b/test/checkout.test.js index 074b13a..cce2575 100644 --- a/test/checkout.test.js +++ b/test/checkout.test.js @@ -168,7 +168,7 @@ describe("plugin", () => { }); test("declares the version WooCommerce reports", () => { - expect(wp(["plugin", "get", "cepis-woocommerce", "--field=version"])).toBe("0.1.0"); + expect(wp(["plugin", "get", "cepis-woocommerce", "--field=version"])).toBe("0.2.0"); }); }); @@ -280,6 +280,47 @@ describe("block checkout", () => { await close(); }, 90000); + + test("a CEP that does not exist names its municipality and still fills nothing", async () => { + const { page, close } = await checkout("/checkout/"); + + await settled(page); + await typeCep(page, "#shipping-postcode", "01001000"); + await page.waitForFunction( + () => document.querySelector("#shipping-address_1").value !== "", + undefined, + { timeout: 15000 }, + ); + + await typeCep(page, "#shipping-postcode", "25845000"); + await page.waitForFunction( + () => + document.body.innerText.includes( + "would be in Areal/RJ, which has CEPs from 25845-001 to 25849-899", + ), + undefined, + { timeout: 15000 }, + ); + + const after = await page.evaluate(() => ({ + street: document.querySelector("#shipping-address_1").value, + city: document.querySelector("#shipping-city").value, + state: document.querySelector("#shipping-state").value, + store: JSON.stringify(window.wp.data.select("wc/store/cart").getCustomerData()), + })); + + // The address the previous, real CEP produced is still there, untouched. + expect(after.street).toBe("Praça da Sé"); + expect(after.city).toBe("São Paulo"); + expect(after.state).toBe("SP"); + + // And nothing from the 404 reached the data store the order is built from. + // A municipality named in an error is a guess about a typo, not an address. + expect(after.store).not.toContain("Areal"); + expect(after.store).not.toContain('"state":"RJ"'); + + await close(); + }, 90000); }); describe("classic checkout", () => { @@ -443,6 +484,82 @@ describe("classic checkout", () => { await close(); }, 90000); + + test("a CEP that does not exist names its municipality and still fills nothing", async () => { + const { page, errors, close } = await checkout("/checkout-classico/"); + + await ready(page); + await typeCep(page, "#billing_postcode", "01001000"); + await page.waitForFunction( + () => document.querySelector("#billing_address_1").value !== "", + undefined, + { timeout: 15000 }, + ); + + await typeCep(page, "#billing_postcode", "25845000"); + await page.waitForFunction( + () => + (document.querySelector(".cepis-autofill-status") || {}).textContent === + "CEP not found. This number would be in Areal/RJ, which has CEPs from 25845-001 to 25849-899. Check the number.", + undefined, + { timeout: 15000 }, + ); + + const after = await page.evaluate(() => ({ + street: document.querySelector("#billing_address_1").value, + city: document.querySelector("#billing_city").value, + state: document.querySelector("#billing_state").value, + invalid: document.querySelector("#billing_postcode").getAttribute("aria-invalid"), + leaked: Array.from(document.querySelectorAll("input, select, textarea")) + .map((field) => String(field.value == null ? "" : field.value)) + .filter((value) => /areal|rio de janeiro|^RJ$/i.test(value)), + })); + + // What the previous, real CEP filled in is still there. + expect(after.street).toBe("Praça da Sé"); + expect(after.city).toBe("São Paulo"); + expect(after.state).toBe("SP"); + expect(after.invalid).toBe("true"); + + // The whole point: naming the municipality must not put it in a field. + // Shipping to a town the CEP does not belong to is a parcel gone astray. + expect(after.leaked).toEqual([]); + expect(errors).toEqual([]); + + await close(); + }, 90000); + + test("a faixa it cannot use falls back to the plain message", async () => { + const { page, errors, close } = await checkout("/checkout-classico/"); + const plain = "CEP not found. Check the number or fill the address in by hand."; + + await ready(page); + await typeCep(page, "#billing_postcode", "25849000"); + + await page.waitForFunction( + (expected) => + (document.querySelector(".cepis-autofill-status") || {}).textContent === expected, + plain, + { timeout: 15000 }, + ); + + // Long enough for the follow-up request to have come back and, if it were + // going to, made a mess of the line. + await page.waitForTimeout(3000); + + const after = await page.evaluate(() => ({ + status: (document.querySelector(".cepis-autofill-status") || {}).textContent, + street: document.querySelector("#billing_address_1").value, + city: document.querySelector("#billing_city").value, + })); + + expect(after.status).toBe(plain); + expect(after.street).toBe(""); + expect(after.city).toBe(""); + expect(errors).toEqual([]); + + await close(); + }, 90000); }); describe("proxy mode", () => { @@ -482,7 +599,7 @@ describe("proxy mode", () => { const served = api.state.requests.filter((entry) => entry.cep === "01001000"); expect(served.length).toBe(1); - expect(served[0].userAgent).toContain("cepis-woocommerce/0.1.0"); + expect(served[0].userAgent).toContain("cepis-woocommerce/0.2.0"); expect(errors).toEqual([]); await close(); @@ -516,6 +633,39 @@ describe("proxy mode", () => { expect(api.state.requests.filter((entry) => entry.cep === "20040020").length).toBe(1); }, 120000); + test("the municipality survives the proxy and its cache, and fills nothing", async () => { + api.reset(); + const { page, close } = await checkout("/checkout-classico/"); + + await ready(page); + await typeCep(page, "#billing_postcode", "25845000"); + + await page.waitForFunction( + () => + (document.querySelector(".cepis-autofill-status") || {}).textContent === + "CEP not found. This number would be in Areal/RJ, which has CEPs from 25845-001 to 25849-899. Check the number.", + undefined, + { timeout: 20000 }, + ); + + const after = await page.evaluate(() => ({ + street: document.querySelector("#billing_address_1").value, + leaked: Array.from(document.querySelectorAll("input, select, textarea")) + .map((field) => String(field.value == null ? "" : field.value)) + .filter((value) => /areal|rio de janeiro|^RJ$/i.test(value)), + })); + + expect(after.street).toBe(""); + expect(after.leaked).toEqual([]); + + // One request reached the API. The lookup itself made it; the follow-up + // that reads the municipality out of the 404 body was served from the + // transient, which is only possible if the miss was cached with it. + expect(api.state.requests.filter((entry) => entry.cep === "25845000")).toHaveLength(1); + + await close(); + }, 90000); + test("the REST route refuses a request without a valid token", async () => { const context = await browser.newContext(); const page = await context.newPage(); @@ -564,6 +714,23 @@ describe("translations", () => { await close(); }, 90000); + + test("so is the message that names the municipality", async () => { + const { page, close } = await checkout("/checkout-classico/"); + + await ready(page); + await typeCep(page, "#billing_postcode", "25845000"); + + await page.waitForFunction( + () => + (document.querySelector(".cepis-autofill-status") || {}).textContent === + "CEP não encontrado. Esse número seria de Areal/RJ, que tem CEPs de 25845-001 a 25849-899. Confira o número.", + undefined, + { timeout: 15000 }, + ); + + await close(); + }, 90000); }); describe("housekeeping", () => { diff --git a/test/fake-api.js b/test/fake-api.js index 4b6ac39..d787bf1 100644 --- a/test/fake-api.js +++ b/test/fake-api.js @@ -43,6 +43,39 @@ const ADDRESSES = { }, }; +/** + * The municipality a CEP that does not exist would have belonged to, keyed by + * the CEP that provokes it. + * + * `25845000` is the real-world shape of the whole problem: Areal/RJ answers + * from 25845-001 upwards and never at the `-000` a shopper reaches for. Any + * other missing CEP — `99999999` — belongs to no municipality at all, and the + * API then omits `faixa` entirely rather than sending it as null, which is what + * the plugin's fallback path has to keep working against. + */ +const RANGES = { + 25845000: { + localidade: "Areal", + uf: "RJ", + estado: "Rio de Janeiro", + regiao: "Sudeste", + ibge: "3300225", + cep_ini: "25845001", + cep_fim: "25849899", + ceps: 118, + }, + + // A faixa with no municipality in it. The real API does not send this, and + // the point of having it here is that the checkout must not care: an + // unusable faixa has to end in exactly the message the plugin has always + // shown, not in a broken sentence and not in a JavaScript error. + 25849000: { + uf: "RJ", + cep_ini: "25845001", + cep_fim: "25849899", + }, +}; + const HEADERS = { "Access-Control-Allow-Origin": "*", "Content-Type": "application/json; charset=utf-8", @@ -76,10 +109,13 @@ export function startFakeApi(port) { const address = ADDRESSES[cep]; if (!address) { - return new Response( - JSON.stringify({ erro: "cep_nao_encontrado", mensagem: "CEP não existe na base do DNE" }), - { status: 404, headers: HEADERS }, - ); + const missing = { erro: "cep_nao_encontrado", mensagem: "CEP não existe na base do DNE" }; + + if (RANGES[cep]) { + missing.faixa = RANGES[cep]; + } + + return new Response(JSON.stringify(missing), { status: 404, headers: HEADERS }); } return new Response(JSON.stringify(address), { status: 200, headers: HEADERS }); |