summaryrefslogtreecommitdiffstats
path: root/test/server.js
diff options
context:
space:
mode:
authorMurilo Ijanc <[email protected]>2026-07-26 15:01:02 -0300
committerMurilo Ijanc <[email protected]>2026-07-26 15:01:02 -0300
commit36486007ccada8c31d30e8f528d21233cabce32c (patch)
treed14a108845e4c984ec922ba98d153c8d68da6ebb /test/server.js
downloadautofill-36486007ccada8c31d30e8f528d21233cabce32c.tar.gz
Initial importv0.1.0
Diffstat (limited to 'test/server.js')
-rw-r--r--test/server.js171
1 files changed, 171 insertions, 0 deletions
diff --git a/test/server.js b/test/server.js
new file mode 100644
index 0000000..720f4a0
--- /dev/null
+++ b/test/server.js
@@ -0,0 +1,171 @@
+/**
+ * A fake cep.is, stood up with `Bun.serve` so the browser tests never touch the
+ * network. Same shapes and status codes as the real API, plus the counters the
+ * tests assert on.
+ *
+ * @module
+ */
+
+const ADDRESSES = {
+ "01001000": {
+ cep: "01001000",
+ logradouro: "Praça da Sé",
+ complemento: "lado ímpar",
+ unidade: "",
+ bairro: "Sé",
+ localidade: "São Paulo",
+ uf: "SP",
+ estado: "São Paulo",
+ regiao: "Sudeste",
+ ibge: "3550308",
+ gia: "1004",
+ ddd: "11",
+ siafi: "7107",
+ geo: { lat: -23.567202, lon: -46.649659, raio_m: 1084, precisao: "logradouro" },
+ },
+ 20040020: {
+ cep: "20040020",
+ logradouro: "Avenida Rio Branco",
+ complemento: "até 0999 - lado ímpar",
+ unidade: "",
+ bairro: "Centro",
+ localidade: "Rio de Janeiro",
+ uf: "RJ",
+ estado: "Rio de Janeiro",
+ regiao: "Sudeste",
+ ibge: "3304557",
+ gia: "",
+ ddd: "21",
+ siafi: "6001",
+ },
+};
+
+const CORS = {
+ "Access-Control-Allow-Origin": "*",
+ "Content-Type": "application/json; charset=utf-8",
+};
+
+/**
+ * Start the fake API on an ephemeral port.
+ *
+ * The returned `state` is live: set `state.delays[cep]` to hold a response
+ * back, or `state.force` to `{status}` to make every route fail. `requests`
+ * lists every CEP asked for, and `aborted` counts the requests the browser
+ * dropped mid-flight.
+ *
+ * @returns {{url: string, state: {requests: string[], aborted: number, delays: Record<string, number>, force: {status: number}|null}, reset: () => void, stop: () => void}}
+ */
+export function startApi() {
+ const state = { requests: [], aborted: 0, delays: {}, force: null };
+
+ const server = Bun.serve({
+ port: 0,
+ hostname: "127.0.0.1",
+ async fetch(request) {
+ const path = new URL(request.url).pathname.slice(1);
+ state.requests.push(path);
+ request.signal.addEventListener("abort", () => {
+ state.aborted += 1;
+ });
+
+ const delay = state.delays[path] ?? 0;
+ if (delay > 0) {
+ await Bun.sleep(delay);
+ }
+ if (request.signal.aborted) {
+ return new Response(null, { status: 499 });
+ }
+
+ if (state.force) {
+ return new Response(JSON.stringify({ erro: "interno", mensagem: "falha interna" }), {
+ status: state.force.status,
+ headers: {
+ ...CORS,
+ ...(state.force.status === 429 ? { "Retry-After": "3" } : {}),
+ },
+ });
+ }
+ if (!/^\d{8}$/.test(path)) {
+ return new Response(
+ JSON.stringify({ erro: "cep_invalido", mensagem: "CEP deve ter 8 dígitos" }),
+ { status: 400, headers: CORS },
+ );
+ }
+ if (path === "88888888") {
+ return new Response(JSON.stringify({ erro: "interno", mensagem: "falha interna" }), {
+ status: 500,
+ headers: CORS,
+ });
+ }
+ if (path === "77777777") {
+ return new Response(JSON.stringify({ erro: "limite", mensagem: "muitas consultas" }), {
+ status: 429,
+ headers: { ...CORS, "Retry-After": "3" },
+ });
+ }
+ const address = ADDRESSES[path];
+ if (!address) {
+ return new Response(
+ JSON.stringify({ erro: "cep_nao_encontrado", mensagem: "CEP não existe na base do DNE" }),
+ { status: 404, headers: CORS },
+ );
+ }
+ return new Response(JSON.stringify(address), { status: 200, headers: CORS });
+ },
+ });
+
+ return {
+ url: `http://127.0.0.1:${server.port}`,
+ state,
+ reset() {
+ state.requests.length = 0;
+ state.aborted = 0;
+ state.delays = {};
+ state.force = null;
+ },
+ stop() {
+ server.stop(true);
+ },
+ };
+}
+
+/**
+ * Serve the checkout fixture page together with a freshly built `<script>`
+ * bundle of the widget, so the tests exercise the file consumers actually
+ * drop into a page.
+ *
+ * @returns {Promise<{url: string, stop: () => void}>}
+ */
+export async function startPage() {
+ const build = await Bun.build({
+ entrypoints: [new URL("../src/global.js", import.meta.url).pathname],
+ target: "browser",
+ format: "iife",
+ });
+ if (!build.success) {
+ throw new AggregateError(build.logs, "could not build the widget");
+ }
+ const bundle = await build.outputs[0].text();
+ const fixture = await Bun.file(new URL("./fixture.html", import.meta.url).pathname).text();
+
+ const server = Bun.serve({
+ port: 0,
+ hostname: "127.0.0.1",
+ fetch(request) {
+ const path = new URL(request.url).pathname;
+ if (path === "/cepis-autofill.js") {
+ return new Response(bundle, {
+ headers: { "Content-Type": "text/javascript; charset=utf-8" },
+ });
+ }
+ return new Response(fixture, { headers: { "Content-Type": "text/html; charset=utf-8" } });
+ },
+ });
+
+ return {
+ url: `http://127.0.0.1:${server.port}/`,
+ stop() {
+ server.stop(true);
+ },
+ };
+}