@excom/fetchable-element 0.1.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 (29) hide show
  1. package/.rush/temp/chunked-rush-logs/fetchable-element.apply-exports.chunks.jsonl +1 -0
  2. package/.rush/temp/chunked-rush-logs/fetchable-element.build_docs.chunks.jsonl +1 -0
  3. package/.rush/temp/chunked-rush-logs/fetchable-element.build_package-metas.chunks.jsonl +1 -0
  4. package/.rush/temp/operation/apply-exports/all.log +1 -0
  5. package/.rush/temp/operation/apply-exports/log-chunks.jsonl +1 -0
  6. package/.rush/temp/operation/apply-exports/state.json +3 -0
  7. package/.rush/temp/operation/build_docs/all.log +1 -0
  8. package/.rush/temp/operation/build_docs/log-chunks.jsonl +1 -0
  9. package/.rush/temp/operation/build_docs/state.json +3 -0
  10. package/.rush/temp/operation/build_package-metas/all.log +1 -0
  11. package/.rush/temp/operation/build_package-metas/log-chunks.jsonl +1 -0
  12. package/.rush/temp/operation/build_package-metas/state.json +3 -0
  13. package/.rush/temp/shrinkwrap-deps.json +3 -0
  14. package/config/rig.json +6 -0
  15. package/index.ts +399 -0
  16. package/package.json +48 -0
  17. package/rush-logs/fetchable-element.apply-exports.cache.log +1 -0
  18. package/rush-logs/fetchable-element.apply-exports.log +1 -0
  19. package/rush-logs/fetchable-element.build_docs.cache.log +1 -0
  20. package/rush-logs/fetchable-element.build_docs.log +1 -0
  21. package/rush-logs/fetchable-element.build_package-metas.cache.log +1 -0
  22. package/rush-logs/fetchable-element.build_package-metas.log +1 -0
  23. package/support/custom-elements.json +368 -0
  24. package/support/dist-docs/fetchable-element.md +120 -0
  25. package/support/docs/README.md +67 -0
  26. package/support/package-meta.json +218 -0
  27. package/support/tests/fetch-lifecycle.test.ts +401 -0
  28. package/support/tests/fetchable-element.test.ts +184 -0
  29. package/tsconfig.json +5 -0
@@ -0,0 +1,184 @@
1
+ import { FetchableElement } from "../../index";
2
+ import {
3
+ afterEach,
4
+ describe,
5
+ expect,
6
+ fixture,
7
+ it,
8
+ vi,
9
+ } from "@excom/heft-rig/profiles/default/config/test-utils";
10
+ import { KitLogger } from "@excom/kit-logger";
11
+
12
+ const TAG = "fetchable-element-test";
13
+ if (!customElements.get(TAG)) {
14
+ FetchableElement.define(TAG);
15
+ }
16
+
17
+ describe("FetchableElement", () => {
18
+ afterEach(() => {
19
+ document.body.innerHTML = "";
20
+ vi.restoreAllMocks();
21
+ });
22
+
23
+ it("defines a custom element tag", () => {
24
+ expect(customElements.get(TAG)).toBeTruthy();
25
+ });
26
+
27
+ it("starts with default state", () => {
28
+ const el = fixture<any>(`<${TAG}></${TAG}>`);
29
+ expect(el).dom.to.equalTag(`<${TAG}></${TAG}>`);
30
+ expect(el.provision).toBeFalsy();
31
+ });
32
+
33
+ it("has default header values", () => {
34
+ const el = fixture<any>(`<${TAG}></${TAG}>`);
35
+ expect(el.headerAccept).toBe("application/json");
36
+ expect(el.headerContentType).toBe("application/json");
37
+ });
38
+
39
+ it("sets loading state", () => {
40
+ const el = fixture<any>(`<${TAG}></${TAG}>`);
41
+ vi.spyOn(globalThis, "fetch").mockImplementation(
42
+ () => new Promise(() => {}),
43
+ );
44
+ el.setLoadingState("http://localhost/api", {});
45
+ expect(el).dom.to.equalTag(`<${TAG} is-loading></${TAG}>`);
46
+ });
47
+
48
+ it("sets success state", () => {
49
+ const el = fixture<any>(`<${TAG}></${TAG}>`);
50
+ const data = { status: 200, body: { result: "ok" } };
51
+ el.setSuccessState(data);
52
+ expect(el).dom.to.equalTag(`<${TAG} is-success></${TAG}>`);
53
+ expect(el.provision).toEqual(data);
54
+ });
55
+
56
+ it("sets error state", () => {
57
+ KitLogger.suppress();
58
+ const el = fixture<any>(`<${TAG}></${TAG}>`);
59
+ const errorData = { status: 500, message: "Server error" };
60
+ el.setErrorState(errorData);
61
+ expect(el).dom.to.equalTag(`<${TAG} is-error></${TAG}>`);
62
+ expect(el.provision).toEqual(errorData);
63
+ KitLogger.unsuppress();
64
+ });
65
+
66
+ it("setCanceledState resets state when fetch is active", () => {
67
+ const el = fixture<any>(`<${TAG}></${TAG}>`);
68
+ el.fetchPromise = Promise.resolve();
69
+ el.setCanceledState();
70
+ expect(el).dom.to.equalTag(`<${TAG}></${TAG}>`);
71
+ });
72
+
73
+ it("setCanceledState is a no-op when no active fetch", () => {
74
+ const el = fixture<any>(`<${TAG}></${TAG}>`);
75
+ const result = el.setCanceledState();
76
+ expect(result).toBeFalsy();
77
+ });
78
+
79
+ it("aborts fetch when new fetch is started", () => {
80
+ const el = fixture<any>(`<${TAG}></${TAG}>`);
81
+ const fetchSpy = vi.spyOn(globalThis, "fetch").mockImplementation(
82
+ () => new Promise(() => {}),
83
+ );
84
+ el.setLoadingState("http://localhost/api/first", {});
85
+ const firstSignal = el.abortController.signal;
86
+ el.doFetch(["http://localhost/api/second", {}]);
87
+ expect(firstSignal.aborted).toBe(true);
88
+ expect(el.abortController.signal.aborted).toBe(false);
89
+ expect(fetchSpy).toHaveBeenCalledTimes(2);
90
+ });
91
+
92
+ it("getFormElement returns null when no formRef", () => {
93
+ const el = fixture<any>(`<${TAG}></${TAG}>`);
94
+ expect(el.getFormElement()).toBeNull();
95
+ });
96
+
97
+ it("getFormElement finds child form with default selector", () => {
98
+ const el = fixture<any>(
99
+ `<${TAG} form-ref=":scope form"><form></form></${TAG}>`,
100
+ );
101
+ const form = el.getFormElement();
102
+ expect(form).toBeInstanceOf(HTMLFormElement);
103
+ });
104
+
105
+ it("getFetchArgs builds correct GET request", () => {
106
+ const el = fixture<any>(
107
+ `<${TAG} api-url="/api/items" api-method="get"></${TAG}>`,
108
+ );
109
+ expect(el).dom.to.equalTag(
110
+ `<${TAG} api-url="/api/items" api-method="get"></${TAG}>`,
111
+ );
112
+ const args = el.getFetchArgs();
113
+ expect(args).toHaveLength(2);
114
+ expect(args[1].method).toBe("GET");
115
+ expect(args[1].headers.Accept).toBe("application/json");
116
+ expect(args[1].credentials).toBe("include");
117
+ expect(args[1].headers["Content-Type"]).toBeUndefined();
118
+ });
119
+
120
+ it("getFetchArgs builds correct POST request with body", () => {
121
+ const el = fixture<any>(
122
+ `<${TAG} api-url="/api/items" api-method="post" form-ref=":scope form">
123
+ <form><input name="name" value="test" /></form>
124
+ </${TAG}>`,
125
+ );
126
+ const args = el.getFetchArgs();
127
+ expect(args[1].method).toBe("POST");
128
+ expect(args[1].headers["Content-Type"]).toBe("application/json");
129
+ expect(args[1].body).toBeDefined();
130
+ });
131
+
132
+ it("getFetchArgs uses PUT as body-carrying method", () => {
133
+ const el = fixture<any>(
134
+ `<${TAG} api-url="/api/items" api-method="put"></${TAG}>`,
135
+ );
136
+ const args = el.getFetchArgs();
137
+ expect(args[1].method).toBe("PUT");
138
+ expect(args[1].headers["Content-Type"]).toBe("application/json");
139
+ });
140
+
141
+ it("getFetchArgs uses PATCH as body-carrying method", () => {
142
+ const el = fixture<any>(
143
+ `<${TAG} api-url="/api/items" api-method="patch"></${TAG}>`,
144
+ );
145
+ const args = el.getFetchArgs();
146
+ expect(args[1].method).toBe("PATCH");
147
+ expect(args[1].headers["Content-Type"]).toBe("application/json");
148
+ });
149
+
150
+ it("getFetchArgs respects custom headers", () => {
151
+ const el = fixture<any>(
152
+ `<${TAG} api-url="/api/items" header-accept="text/html" header-cache-control="no-cache"></${TAG}>`,
153
+ );
154
+ const args = el.getFetchArgs();
155
+ expect(args[1].headers.Accept).toBe("text/html");
156
+ expect(args[1].headers["Cache-Control"]).toBe("no-cache");
157
+ });
158
+
159
+ it("getFetchArgs respects custom credentials", () => {
160
+ const el = fixture<any>(
161
+ `<${TAG} api-url="/api/items" fetch-credentials="same-origin"></${TAG}>`,
162
+ );
163
+ const args = el.getFetchArgs();
164
+ expect(args[1].credentials).toBe("same-origin");
165
+ });
166
+
167
+ it("getFetchArgs defaults to GET method", () => {
168
+ const el = fixture<any>(`<${TAG} api-url="/api/items"></${TAG}>`);
169
+ const args = el.getFetchArgs();
170
+ expect(args[1].method).toBe("GET");
171
+ });
172
+
173
+ it("getFetchArgs defaults credentials to include", () => {
174
+ const el = fixture<any>(`<${TAG} api-url="/api/items"></${TAG}>`);
175
+ const args = el.getFetchArgs();
176
+ expect(args[1].credentials).toBe("include");
177
+ });
178
+
179
+ it("doFetch rejects when no fetch args provided", () => {
180
+ const el = fixture<any>(`<${TAG}></${TAG}>`);
181
+ const result = el.doFetch([]);
182
+ expect(result).toBeFalsy();
183
+ });
184
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "@excom/heft-rig/profiles/default/config/tsconfig.json",
3
+ "include": ["./*.ts"],
4
+ "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
5
+ }