@decocms/apps-vtex 7.2.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/package.json +67 -0
- package/src/README.md +6 -0
- package/src/__tests__/client-segment-cookie.test.ts +255 -0
- package/src/__tests__/client-set-cookie-forward.test.ts +257 -0
- package/src/actions/address.ts +250 -0
- package/src/actions/analytics/sendEvent.ts +86 -0
- package/src/actions/auth.ts +333 -0
- package/src/actions/checkout.ts +522 -0
- package/src/actions/index.ts +11 -0
- package/src/actions/masterData.ts +168 -0
- package/src/actions/misc.ts +188 -0
- package/src/actions/newsletter.ts +105 -0
- package/src/actions/orders.ts +34 -0
- package/src/actions/profile.ts +201 -0
- package/src/actions/session.ts +85 -0
- package/src/actions/trigger.ts +43 -0
- package/src/actions/wishlist.ts +114 -0
- package/src/client.ts +667 -0
- package/src/commerceLoaders.ts +261 -0
- package/src/hooks/__tests__/createUseCart.test.ts +184 -0
- package/src/hooks/__tests__/createUseUser.test.ts +48 -0
- package/src/hooks/__tests__/createUseWishlist.test.ts +87 -0
- package/src/hooks/createUseCart.ts +360 -0
- package/src/hooks/createUseUser.ts +153 -0
- package/src/hooks/createUseWishlist.ts +242 -0
- package/src/hooks/index.ts +19 -0
- package/src/hooks/useAutocomplete.ts +83 -0
- package/src/hooks/useCart.ts +243 -0
- package/src/hooks/useUser.ts +78 -0
- package/src/hooks/useWishlist.ts +119 -0
- package/src/index.ts +17 -0
- package/src/invoke.ts +181 -0
- package/src/loaders/ProductDetailsPage.ts +1 -0
- package/src/loaders/ProductList.ts +1 -0
- package/src/loaders/ProductListingPage.ts +1 -0
- package/src/loaders/address.ts +115 -0
- package/src/loaders/autocomplete.ts +58 -0
- package/src/loaders/brands.ts +44 -0
- package/src/loaders/cart.ts +52 -0
- package/src/loaders/catalog.ts +163 -0
- package/src/loaders/collections.ts +55 -0
- package/src/loaders/index.ts +19 -0
- package/src/loaders/intelligentSearch/__tests__/productListingPage.test.ts +20 -0
- package/src/loaders/intelligentSearch/productDetailsPage.ts +95 -0
- package/src/loaders/intelligentSearch/productList.ts +154 -0
- package/src/loaders/intelligentSearch/productListingPage.ts +506 -0
- package/src/loaders/intelligentSearch/suggestions.ts +46 -0
- package/src/loaders/legacy/productDetailsPage.ts +1 -0
- package/src/loaders/legacy/productList.ts +1 -0
- package/src/loaders/legacy/relatedProductsLoader.ts +81 -0
- package/src/loaders/legacy.ts +635 -0
- package/src/loaders/logistics.ts +111 -0
- package/src/loaders/minicart.ts +78 -0
- package/src/loaders/navbar.ts +26 -0
- package/src/loaders/orders.ts +92 -0
- package/src/loaders/pageType.ts +68 -0
- package/src/loaders/payment.ts +97 -0
- package/src/loaders/productListFull.ts +159 -0
- package/src/loaders/profile.ts +138 -0
- package/src/loaders/promotion.ts +30 -0
- package/src/loaders/search.ts +124 -0
- package/src/loaders/session.ts +83 -0
- package/src/loaders/user.ts +87 -0
- package/src/loaders/wishlist.ts +89 -0
- package/src/loaders/wishlistProducts.ts +69 -0
- package/src/loaders/workflow/products.ts +64 -0
- package/src/loaders/workflow.ts +316 -0
- package/src/logo.png +0 -0
- package/src/middleware.ts +240 -0
- package/src/mod.ts +152 -0
- package/src/registry.ts +9 -0
- package/src/types.ts +248 -0
- package/src/utils/__tests__/cookieSanitizer.test.ts +162 -0
- package/src/utils/__tests__/fetch.test.ts +80 -0
- package/src/utils/__tests__/fetchCache.test.ts +112 -0
- package/src/utils/__tests__/instrumentedFetch.test.ts +158 -0
- package/src/utils/__tests__/intelligentSearch.test.ts +88 -0
- package/src/utils/__tests__/minicart.test.ts +184 -0
- package/src/utils/__tests__/operationRouter.test.ts +227 -0
- package/src/utils/__tests__/resourceRange.test.ts +32 -0
- package/src/utils/__tests__/sitemap.test.ts +185 -0
- package/src/utils/__tests__/slugify.test.ts +31 -0
- package/src/utils/__tests__/transform.test.ts +698 -0
- package/src/utils/accountLoaders.ts +203 -0
- package/src/utils/authHelpers.ts +85 -0
- package/src/utils/batch.ts +18 -0
- package/src/utils/cookieSanitizer.ts +173 -0
- package/src/utils/cookies.ts +167 -0
- package/src/utils/enrichment.ts +560 -0
- package/src/utils/fetch.ts +107 -0
- package/src/utils/fetchCache.ts +222 -0
- package/src/utils/index.ts +19 -0
- package/src/utils/instrumentedFetch.ts +78 -0
- package/src/utils/intelligentSearch.ts +96 -0
- package/src/utils/legacy.ts +139 -0
- package/src/utils/minicart.ts +139 -0
- package/src/utils/operationRouter.ts +139 -0
- package/src/utils/pickAndOmit.ts +25 -0
- package/src/utils/proxy.ts +435 -0
- package/src/utils/resourceRange.ts +10 -0
- package/src/utils/segment.ts +152 -0
- package/src/utils/similars.ts +37 -0
- package/src/utils/sitemap.ts +282 -0
- package/src/utils/slugCache.ts +28 -0
- package/src/utils/slugify.ts +13 -0
- package/src/utils/transform.ts +1509 -0
- package/src/utils/types.ts +1884 -0
- package/src/utils/vtexId.ts +138 -0
- package/tsconfig.json +7 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { configureVtex } from "../../client";
|
|
3
|
+
import { createVtexSitemapProxy, isVtexSitemapPath } from "../sitemap";
|
|
4
|
+
|
|
5
|
+
const ACCOUNT = "myaccount";
|
|
6
|
+
const VTEX_HOST = `${ACCOUNT}.vtexcommercestable.com.br`;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
configureVtex({ account: ACCOUNT });
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
vi.restoreAllMocks();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe("isVtexSitemapPath", () => {
|
|
17
|
+
it.each([
|
|
18
|
+
["/sitemap.xml", true],
|
|
19
|
+
["/sitemap/products-1.xml", true],
|
|
20
|
+
["/sitemap/category-3.xml", true],
|
|
21
|
+
["/sitemap/", true],
|
|
22
|
+
["/sitemap", false],
|
|
23
|
+
["/", false],
|
|
24
|
+
["/checkout", false],
|
|
25
|
+
["/sitemap-busca.xml", false],
|
|
26
|
+
])("%s → %s", (pathname, expected) => {
|
|
27
|
+
expect(isVtexSitemapPath(pathname)).toBe(expected);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe("createVtexSitemapProxy", () => {
|
|
32
|
+
function makeFetch(
|
|
33
|
+
responseBody: string,
|
|
34
|
+
init: { status?: number; ok?: boolean } = {},
|
|
35
|
+
): typeof fetch {
|
|
36
|
+
const status = init.status ?? 200;
|
|
37
|
+
return vi.fn(
|
|
38
|
+
async () =>
|
|
39
|
+
new Response(responseBody, {
|
|
40
|
+
status,
|
|
41
|
+
headers: { "content-type": "application/xml" },
|
|
42
|
+
}),
|
|
43
|
+
) as unknown as typeof fetch;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const SITEMAP_INDEX = `<?xml version="1.0" encoding="UTF-8"?>
|
|
47
|
+
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
48
|
+
<sitemap><loc>https://${VTEX_HOST}/sitemap/products-1.xml</loc></sitemap>
|
|
49
|
+
<sitemap><loc>https://${VTEX_HOST}/sitemap/category-1.xml</loc></sitemap>
|
|
50
|
+
</sitemapindex>`;
|
|
51
|
+
|
|
52
|
+
const PRODUCT_SUB_SITEMAP = `<?xml version="1.0" encoding="UTF-8"?>
|
|
53
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
54
|
+
<url><loc>https://${VTEX_HOST}/p/some-product/p</loc></url>
|
|
55
|
+
</urlset>`;
|
|
56
|
+
|
|
57
|
+
it("returns null for non-sitemap paths", async () => {
|
|
58
|
+
const proxy = createVtexSitemapProxy({ fetchImpl: makeFetch("") });
|
|
59
|
+
const url = new URL("https://www.mystore.com/checkout");
|
|
60
|
+
await expect(proxy(new Request(url), url)).resolves.toBeNull();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("proxies /sitemap.xml and rewrites VTEX hostname to storefront origin", async () => {
|
|
64
|
+
const fetchImpl = makeFetch(SITEMAP_INDEX);
|
|
65
|
+
const proxy = createVtexSitemapProxy({ fetchImpl });
|
|
66
|
+
const url = new URL("https://www.mystore.com/sitemap.xml");
|
|
67
|
+
|
|
68
|
+
const res = await proxy(new Request(url), url);
|
|
69
|
+
|
|
70
|
+
expect(res).not.toBeNull();
|
|
71
|
+
expect(res!.status).toBe(200);
|
|
72
|
+
expect(res!.headers.get("content-type")).toBe("application/xml; charset=utf-8");
|
|
73
|
+
expect(fetchImpl).toHaveBeenCalledWith(`https://${VTEX_HOST}/sitemap.xml`);
|
|
74
|
+
|
|
75
|
+
const xml = await res!.text();
|
|
76
|
+
expect(xml).not.toContain(VTEX_HOST);
|
|
77
|
+
expect(xml).toContain("https://www.mystore.com/sitemap/products-1.xml");
|
|
78
|
+
expect(xml).toContain("https://www.mystore.com/sitemap/category-1.xml");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("proxies /sitemap/* sub-sitemaps with hostname rewrite", async () => {
|
|
82
|
+
const fetchImpl = makeFetch(PRODUCT_SUB_SITEMAP);
|
|
83
|
+
const proxy = createVtexSitemapProxy({ fetchImpl });
|
|
84
|
+
const url = new URL("https://www.mystore.com/sitemap/products-1.xml");
|
|
85
|
+
|
|
86
|
+
const res = await proxy(new Request(url), url);
|
|
87
|
+
|
|
88
|
+
expect(res).not.toBeNull();
|
|
89
|
+
expect(fetchImpl).toHaveBeenCalledWith(`https://${VTEX_HOST}/sitemap/products-1.xml`);
|
|
90
|
+
const xml = await res!.text();
|
|
91
|
+
expect(xml).toContain("https://www.mystore.com/p/some-product/p");
|
|
92
|
+
expect(xml).not.toContain(VTEX_HOST);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("injects extraSitemaps entries into /sitemap.xml only", async () => {
|
|
96
|
+
const fetchImpl = makeFetch(SITEMAP_INDEX);
|
|
97
|
+
const proxy = createVtexSitemapProxy({
|
|
98
|
+
fetchImpl,
|
|
99
|
+
extraSitemaps: ["/sitemap-busca.xml", "extra-bare", "https://cdn.example.com/static.xml"],
|
|
100
|
+
});
|
|
101
|
+
const url = new URL("https://www.mystore.com/sitemap.xml");
|
|
102
|
+
const xml = await (await proxy(new Request(url), url))!.text();
|
|
103
|
+
|
|
104
|
+
expect(xml).toContain("<loc>https://www.mystore.com/sitemap-busca.xml</loc>");
|
|
105
|
+
expect(xml).toContain("<loc>https://www.mystore.com/extra-bare</loc>");
|
|
106
|
+
expect(xml).toContain("<loc>https://cdn.example.com/static.xml</loc>");
|
|
107
|
+
// Extra entries are inserted before the closing tag (i.e. inside the index).
|
|
108
|
+
expect(xml.indexOf("sitemap-busca.xml")).toBeLessThan(xml.indexOf("</sitemapindex>"));
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("does not inject extraSitemaps into sub-sitemaps", async () => {
|
|
112
|
+
const fetchImpl = makeFetch(PRODUCT_SUB_SITEMAP);
|
|
113
|
+
const proxy = createVtexSitemapProxy({
|
|
114
|
+
fetchImpl,
|
|
115
|
+
extraSitemaps: ["/sitemap-busca.xml"],
|
|
116
|
+
});
|
|
117
|
+
const url = new URL("https://www.mystore.com/sitemap/products-1.xml");
|
|
118
|
+
const xml = await (await proxy(new Request(url), url))!.text();
|
|
119
|
+
|
|
120
|
+
expect(xml).not.toContain("sitemap-busca.xml");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("returns 502 when VTEX origin returns non-OK", async () => {
|
|
124
|
+
const fetchImpl = makeFetch("upstream is down", { status: 503 });
|
|
125
|
+
const proxy = createVtexSitemapProxy({ fetchImpl });
|
|
126
|
+
const url = new URL("https://www.mystore.com/sitemap.xml");
|
|
127
|
+
const res = await proxy(new Request(url), url);
|
|
128
|
+
expect(res!.status).toBe(502);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("returns 502 when fetch throws", async () => {
|
|
132
|
+
const fetchImpl = vi.fn(async () => {
|
|
133
|
+
throw new Error("network down");
|
|
134
|
+
}) as unknown as typeof fetch;
|
|
135
|
+
const proxy = createVtexSitemapProxy({ fetchImpl });
|
|
136
|
+
const url = new URL("https://www.mystore.com/sitemap.xml");
|
|
137
|
+
const res = await proxy(new Request(url), url);
|
|
138
|
+
expect(res!.status).toBe(502);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("uses the configured environment", async () => {
|
|
142
|
+
const fetchImpl = makeFetch(SITEMAP_INDEX);
|
|
143
|
+
const proxy = createVtexSitemapProxy({
|
|
144
|
+
fetchImpl,
|
|
145
|
+
environment: "vtexcommercebeta",
|
|
146
|
+
});
|
|
147
|
+
const url = new URL("https://www.mystore.com/sitemap.xml");
|
|
148
|
+
await proxy(new Request(url), url);
|
|
149
|
+
|
|
150
|
+
expect(fetchImpl).toHaveBeenCalledWith(
|
|
151
|
+
`https://${ACCOUNT}.vtexcommercebeta.com.br/sitemap.xml`,
|
|
152
|
+
);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("honors a custom Cache-Control header", async () => {
|
|
156
|
+
const fetchImpl = makeFetch(SITEMAP_INDEX);
|
|
157
|
+
const proxy = createVtexSitemapProxy({
|
|
158
|
+
fetchImpl,
|
|
159
|
+
cacheControl: "public, max-age=60",
|
|
160
|
+
});
|
|
161
|
+
const url = new URL("https://www.mystore.com/sitemap.xml");
|
|
162
|
+
const res = await proxy(new Request(url), url);
|
|
163
|
+
expect(res!.headers.get("cache-control")).toBe("public, max-age=60");
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("emits the default Cache-Control by default", async () => {
|
|
167
|
+
const fetchImpl = makeFetch(SITEMAP_INDEX);
|
|
168
|
+
const proxy = createVtexSitemapProxy({ fetchImpl });
|
|
169
|
+
const url = new URL("https://www.mystore.com/sitemap.xml");
|
|
170
|
+
const res = await proxy(new Request(url), url);
|
|
171
|
+
expect(res!.headers.get("cache-control")).toBe(
|
|
172
|
+
"public, s-maxage=3600, stale-while-revalidate=86400",
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("respects non-default VTEX domain (e.g. .com)", async () => {
|
|
177
|
+
configureVtex({ account: ACCOUNT, domain: "com" });
|
|
178
|
+
const fetchImpl = makeFetch(SITEMAP_INDEX);
|
|
179
|
+
const proxy = createVtexSitemapProxy({ fetchImpl });
|
|
180
|
+
const url = new URL("https://www.mystore.com/sitemap.xml");
|
|
181
|
+
await proxy(new Request(url), url);
|
|
182
|
+
|
|
183
|
+
expect(fetchImpl).toHaveBeenCalledWith(`https://${ACCOUNT}.vtexcommercestable.com/sitemap.xml`);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { slugify } from "../slugify";
|
|
3
|
+
|
|
4
|
+
describe("slugify", () => {
|
|
5
|
+
it("lowercases and removes spaces", () => {
|
|
6
|
+
expect(slugify("Hello World")).toBe("hello-world");
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("replaces accented characters", () => {
|
|
10
|
+
expect(slugify("Calçados")).toBe("calcados");
|
|
11
|
+
expect(slugify("São Paulo")).toBe("sao-paulo");
|
|
12
|
+
expect(slugify("Café")).toBe("cafe");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("replaces special characters with hyphens", () => {
|
|
16
|
+
expect(slugify("shoes/running")).toBe("shoes-running");
|
|
17
|
+
expect(slugify("men's wear")).toBe("men-s-wear");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("removes commas", () => {
|
|
21
|
+
expect(slugify("shoes,sandals")).toBe("shoessandals");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("handles already slugified strings", () => {
|
|
25
|
+
expect(slugify("already-slugified")).toBe("already-slugified");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("handles empty string", () => {
|
|
29
|
+
expect(slugify("")).toBe("");
|
|
30
|
+
});
|
|
31
|
+
});
|