@prosopo/client-bundle-example 2.11.2 → 2.11.4
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/.turbo/turbo-build$colon$cjs.log +4 -7
- package/.turbo/turbo-build$colon$tsc.log +11 -14
- package/.turbo/turbo-build.log +4 -10
- package/CHANGELOG.md +32 -0
- package/LICENSE +201 -0
- package/README.md +6 -6
- package/dist/cjs/plugins/pages.cjs +7 -0
- package/dist/plugins/pages.js +7 -0
- package/env.staging +5 -4
- package/package.json +52 -53
- package/vite.config.ts +50 -1
- package/env.development +0 -7
- package/env.production +0 -6
- package/src/assets/dummy.txt +0 -0
- package/src/frictionless-explicit-web3.html +0 -236
- package/src/frictionless-explicit.html +0 -204
- package/src/frictionless-implicit.html +0 -192
- package/src/frictionless-manual-start.html +0 -204
- package/src/image-explicit-web3.html +0 -182
- package/src/image-explicit.html +0 -151
- package/src/image-implicit.html +0 -192
- package/src/index.html +0 -192
- package/src/index.ts +0 -15
- package/src/invisible-frictionless-explicit.html +0 -123
- package/src/invisible-frictionless-implicit.html +0 -187
- package/src/invisible-image-explicit.html +0 -146
- package/src/invisible-image-implicit.html +0 -187
- package/src/invisible-pow-explicit.html +0 -146
- package/src/invisible-pow-implicit.html +0 -94
- package/src/invisible-puzzle-explicit.html +0 -145
- package/src/invisible-puzzle-implicit.html +0 -94
- package/src/plugins/code-snippet-injector.ts +0 -179
- package/src/plugins/form-filler-injector.ts +0 -148
- package/src/plugins/layout-injector.ts +0 -256
- package/src/plugins/logo.ts +0 -15
- package/src/plugins/pages.ts +0 -219
- package/src/plugins/placement-injector.ts +0 -114
- package/src/plugins/slots.ts +0 -32
- package/src/plugins/status-log-injector.ts +0 -143
- package/src/pow-explicit-web3.html +0 -179
- package/src/pow-explicit.html +0 -143
- package/src/pow-implicit-sessionid.html +0 -209
- package/src/pow-implicit.html +0 -192
- package/src/puzzle-bind-explicit.html +0 -83
- package/src/puzzle-explicit.html +0 -151
- package/src/puzzle-implicit.html +0 -192
- package/src/styles/demo.css +0 -803
- package/src/tests/plugins.unit.test.ts +0 -490
- package/tsconfig.cjs.json +0 -25
- package/tsconfig.json +0 -31
- package/tsconfig.tsbuildinfo +0 -1
- package/tsconfig.types.json +0 -9
|
@@ -1,490 +0,0 @@
|
|
|
1
|
-
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
14
|
-
|
|
15
|
-
// The demo playground's vite plugins rewrite each demo page's HTML at serve and
|
|
16
|
-
// build time. They run over hand-written HTML, so the important behaviour is
|
|
17
|
-
// what they do with malformed or already-transformed input: they must return
|
|
18
|
-
// the html untouched rather than producing a broken page.
|
|
19
|
-
|
|
20
|
-
import { CaptchaType } from "@prosopo/types";
|
|
21
|
-
import type { IndexHtmlTransformContext, Plugin } from "vite";
|
|
22
|
-
import { describe, expect, it } from "vitest";
|
|
23
|
-
import codeSnippetInjector, {
|
|
24
|
-
snippetFor,
|
|
25
|
-
} from "../plugins/code-snippet-injector.js";
|
|
26
|
-
import formFillerInjector from "../plugins/form-filler-injector.js";
|
|
27
|
-
import layoutInjector, { DEMO_HOSTNAME } from "../plugins/layout-injector.js";
|
|
28
|
-
import {
|
|
29
|
-
type DemoRendering,
|
|
30
|
-
demoPages,
|
|
31
|
-
findPageByPath,
|
|
32
|
-
pagePathFromFilename,
|
|
33
|
-
relativePrefix,
|
|
34
|
-
renderingsFor,
|
|
35
|
-
resolvePage,
|
|
36
|
-
} from "../plugins/pages.js";
|
|
37
|
-
import placementInjector from "../plugins/placement-injector.js";
|
|
38
|
-
import { fillSlot, slotMarkup } from "../plugins/slots.js";
|
|
39
|
-
import statusLogInjector from "../plugins/status-log-injector.js";
|
|
40
|
-
|
|
41
|
-
type Handler = (html: string, ctx: IndexHtmlTransformContext) => string;
|
|
42
|
-
|
|
43
|
-
// transformIndexHtml is declared in its object form ({ order, handler }) by
|
|
44
|
-
// every plugin here; pull the handler out so the tests can call it directly.
|
|
45
|
-
const getHandler = (plugin: Plugin): Handler => {
|
|
46
|
-
const transform = plugin.transformIndexHtml;
|
|
47
|
-
if (
|
|
48
|
-
!transform ||
|
|
49
|
-
"function" === typeof transform ||
|
|
50
|
-
!("handler" in transform) ||
|
|
51
|
-
!transform.handler
|
|
52
|
-
) {
|
|
53
|
-
throw new Error(`${plugin.name} has no object-form transformIndexHtml`);
|
|
54
|
-
}
|
|
55
|
-
return transform.handler as unknown as Handler;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const ctx = (filename: string): IndexHtmlTransformContext =>
|
|
59
|
-
({ filename, path: `/${filename}` }) as IndexHtmlTransformContext;
|
|
60
|
-
|
|
61
|
-
const page = (body = "<p>hello</p>"): string =>
|
|
62
|
-
`<html><head><title>demo</title></head><body>${body}</body></html>`;
|
|
63
|
-
|
|
64
|
-
const noBody = "<div>no body here</div>";
|
|
65
|
-
|
|
66
|
-
const cwd = process.cwd();
|
|
67
|
-
|
|
68
|
-
describe("pages", () => {
|
|
69
|
-
it("has one page per setup and one setup per page", () => {
|
|
70
|
-
const setups = demoPages.map(
|
|
71
|
-
({ captchaType, mode, rendering }) =>
|
|
72
|
-
`${captchaType}/${mode}/${rendering}`,
|
|
73
|
-
);
|
|
74
|
-
const paths = demoPages.map(({ path }) => path);
|
|
75
|
-
|
|
76
|
-
expect(new Set(setups).size).toBe(demoPages.length);
|
|
77
|
-
expect(new Set(paths).size).toBe(demoPages.length);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("makes standard implicit frictionless the index page", () => {
|
|
81
|
-
expect(findPageByPath("index.html")).toMatchObject({
|
|
82
|
-
captchaType: CaptchaType.frictionless,
|
|
83
|
-
mode: "standard",
|
|
84
|
-
rendering: "implicit",
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("falls back to implicit rendering when a setup has no page", () => {
|
|
89
|
-
const resolved = resolvePage({
|
|
90
|
-
captchaType: CaptchaType.frictionless,
|
|
91
|
-
mode: "invisible",
|
|
92
|
-
rendering: "manual",
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
expect(resolved.path).toBe("invisible-frictionless-implicit.html");
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it("only offers renderings that have a page", () => {
|
|
99
|
-
const values = (captchaType: CaptchaType.pow | CaptchaType.puzzle) =>
|
|
100
|
-
renderingsFor(captchaType, "standard").map(
|
|
101
|
-
({ value }): DemoRendering => value,
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
expect(values(CaptchaType.pow)).toEqual(["implicit", "explicit"]);
|
|
105
|
-
expect(values(CaptchaType.puzzle)).toEqual([
|
|
106
|
-
"implicit",
|
|
107
|
-
"explicit",
|
|
108
|
-
"bound",
|
|
109
|
-
]);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it("reads the page path from a src, dist or absolute filename", () => {
|
|
113
|
-
expect(pagePathFromFilename("src/index.html")).toBe("index.html");
|
|
114
|
-
expect(pagePathFromFilename(`${cwd}/dist/pow-explicit.html`)).toBe(
|
|
115
|
-
"pow-explicit.html",
|
|
116
|
-
);
|
|
117
|
-
expect(pagePathFromFilename(`${cwd}/src/nested/deep.html`)).toBe(
|
|
118
|
-
"nested/deep.html",
|
|
119
|
-
);
|
|
120
|
-
expect(pagePathFromFilename(undefined)).toBe("");
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it("climbs out of nested pages", () => {
|
|
124
|
-
expect(relativePrefix("index.html")).toBe("");
|
|
125
|
-
expect(relativePrefix("nested/deep.html")).toBe("../");
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
describe("slots", () => {
|
|
130
|
-
it("returns undefined when the slot is not on the page", () => {
|
|
131
|
-
expect(fillSlot(page(), "events", "x")).toBeUndefined();
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it("inserts content literally, even when it looks like a replace pattern", () => {
|
|
135
|
-
const html = fillSlot(page(slotMarkup("events")), "events", "$&");
|
|
136
|
-
|
|
137
|
-
expect(html).toBe(page("$&"));
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
describe("layoutInjector", () => {
|
|
142
|
-
const handler = getHandler(layoutInjector());
|
|
143
|
-
|
|
144
|
-
it("is registered as a pre transform", () => {
|
|
145
|
-
expect(layoutInjector().transformIndexHtml).toMatchObject({
|
|
146
|
-
order: "pre",
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it("wraps the page in the header, sidebar and panel with a slot for each plugin", () => {
|
|
151
|
-
const html = handler(page(), ctx(`${cwd}/src/pow-explicit.html`));
|
|
152
|
-
|
|
153
|
-
expect(html).toContain('<body class="demo">');
|
|
154
|
-
expect(html).toContain('class="demo-header"');
|
|
155
|
-
expect(html).toContain('href="styles/demo.css"');
|
|
156
|
-
for (const slot of ["placement", "toolbar", "events", "code"] as const) {
|
|
157
|
-
expect(html).toContain(slotMarkup(slot));
|
|
158
|
-
}
|
|
159
|
-
expect(html.indexOf("<p>hello</p>")).toBeLessThan(
|
|
160
|
-
html.indexOf('class="demo-panel"'),
|
|
161
|
-
);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it("loads Plausible only when served from the public demo host", () => {
|
|
165
|
-
const html = handler(page(), ctx(`${cwd}/src/pow-explicit.html`));
|
|
166
|
-
const inlineScript = html.match(
|
|
167
|
-
/<script>\s*\(function \(\) \{([\s\S]*?)\}\)\(\);\s*<\/script>/,
|
|
168
|
-
)?.[1];
|
|
169
|
-
expect(inlineScript).toBeDefined();
|
|
170
|
-
|
|
171
|
-
type FakeElement = Record<string, unknown>;
|
|
172
|
-
const loadedFrom = (hostname: string): FakeElement[] => {
|
|
173
|
-
const appended: FakeElement[] = [];
|
|
174
|
-
const fakeDocument = {
|
|
175
|
-
createElement: (): FakeElement => {
|
|
176
|
-
const element: FakeElement = {};
|
|
177
|
-
element.setAttribute = (name: string, value: string): void => {
|
|
178
|
-
element[name] = value;
|
|
179
|
-
};
|
|
180
|
-
return element;
|
|
181
|
-
},
|
|
182
|
-
head: {
|
|
183
|
-
appendChild: (element: FakeElement): void => {
|
|
184
|
-
appended.push(element);
|
|
185
|
-
},
|
|
186
|
-
},
|
|
187
|
-
};
|
|
188
|
-
new Function("location", "document", inlineScript ?? "")(
|
|
189
|
-
{ hostname },
|
|
190
|
-
fakeDocument,
|
|
191
|
-
);
|
|
192
|
-
return appended;
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
expect(loadedFrom("localhost")).toHaveLength(0);
|
|
196
|
-
expect(loadedFrom(`staging.${DEMO_HOSTNAME}`)).toHaveLength(0);
|
|
197
|
-
const [tracker] = loadedFrom(DEMO_HOSTNAME);
|
|
198
|
-
expect(tracker).toMatchObject({
|
|
199
|
-
defer: true,
|
|
200
|
-
"data-domain": DEMO_HOSTNAME,
|
|
201
|
-
src: "https://prosopo.io/js/script.kairee5buy1chae8eit0so8ahphae9Oo.js",
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
it("marks the current type, mode and rendering", () => {
|
|
206
|
-
const html = handler(page(), ctx(`${cwd}/src/pow-explicit.html`));
|
|
207
|
-
|
|
208
|
-
expect(html.match(/aria-current="page"/g)).toHaveLength(3);
|
|
209
|
-
expect(html).toContain(
|
|
210
|
-
'<a class="demo-choice" data-demo-nav href="pow-explicit.html" aria-current="page">',
|
|
211
|
-
);
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
it("folds rendering away unless the page uses a non-default rendering", () => {
|
|
215
|
-
const implicit = handler(page(), ctx(`${cwd}/src/pow-implicit.html`));
|
|
216
|
-
const explicit = handler(page(), ctx(`${cwd}/src/pow-explicit.html`));
|
|
217
|
-
|
|
218
|
-
expect(implicit).toContain('<details class="demo-advanced">');
|
|
219
|
-
expect(explicit).toContain('<details class="demo-advanced" open>');
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
it("keeps the mode and rendering when switching captcha type", () => {
|
|
223
|
-
const html = handler(
|
|
224
|
-
page(),
|
|
225
|
-
ctx(`${cwd}/src/invisible-image-explicit.html`),
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
expect(html).toContain('href="invisible-pow-explicit.html"');
|
|
229
|
-
expect(html).toContain('href="invisible-puzzle-explicit.html"');
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it("marks nothing current and shows no code for a page outside the registry", () => {
|
|
233
|
-
const html = handler(page(), ctx(`${cwd}/src/pow-implicit-sessionid.html`));
|
|
234
|
-
|
|
235
|
-
expect(html).not.toContain('aria-current="page"');
|
|
236
|
-
expect(html).not.toContain(slotMarkup("code"));
|
|
237
|
-
expect(html).toContain('href="index.html"');
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
it("resolves links relative to a nested page", () => {
|
|
241
|
-
const html = handler(page(), ctx(`${cwd}/src/nested/deep.html`));
|
|
242
|
-
|
|
243
|
-
expect(html).toContain('href="../index.html"');
|
|
244
|
-
expect(html).toContain('href="../styles/demo.css"');
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
it("tolerates a context with no filename", () => {
|
|
248
|
-
const html = handler(page(), {} as IndexHtmlTransformContext);
|
|
249
|
-
|
|
250
|
-
expect(html).toContain('class="demo-header"');
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
it("returns the html unchanged when there is no body tag", () => {
|
|
254
|
-
expect(handler(noBody, ctx("src/index.html"))).toBe(noBody);
|
|
255
|
-
});
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
describe("codeSnippetInjector", () => {
|
|
259
|
-
const handler = getHandler(codeSnippetInjector());
|
|
260
|
-
|
|
261
|
-
it("fills the code slot with the escaped snippet for the page", () => {
|
|
262
|
-
const html = handler(
|
|
263
|
-
page(slotMarkup("code")),
|
|
264
|
-
ctx(`${cwd}/src/frictionless-explicit.html`),
|
|
265
|
-
);
|
|
266
|
-
|
|
267
|
-
expect(html).not.toContain(slotMarkup("code"));
|
|
268
|
-
expect(html).toContain("data-demo-copy");
|
|
269
|
-
expect(html).toContain("<div id="procaptcha-container">");
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
it("falls back to the end of the body when there is no slot", () => {
|
|
273
|
-
const html = handler(page(), ctx(`${cwd}/src/index.html`));
|
|
274
|
-
|
|
275
|
-
expect(html.indexOf("data-demo-copy")).toBeLessThan(
|
|
276
|
-
html.indexOf("</body>"),
|
|
277
|
-
);
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
it("gives every demo page a snippet that uses a placeholder site key", () => {
|
|
281
|
-
for (const demoPage of demoPages) {
|
|
282
|
-
expect(snippetFor(demoPage)).toContain("YOUR_SITE_KEY");
|
|
283
|
-
}
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
it("matches the snippet to the rendering and mode", () => {
|
|
287
|
-
const snippet = (path: string): string => {
|
|
288
|
-
const demoPage = findPageByPath(path);
|
|
289
|
-
if (!demoPage) {
|
|
290
|
-
throw new Error(`${path} is not a demo page`);
|
|
291
|
-
}
|
|
292
|
-
return snippetFor(demoPage);
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
expect(snippet("pow-implicit.html")).toContain('class="procaptcha"');
|
|
296
|
-
expect(snippet("frictionless-manual-start.html")).toContain(
|
|
297
|
-
'data-start-mode="manual"',
|
|
298
|
-
);
|
|
299
|
-
expect(snippet("puzzle-bind-explicit.html")).toContain("bind:");
|
|
300
|
-
expect(snippet("invisible-image-implicit.html")).toContain(
|
|
301
|
-
'data-size="invisible"',
|
|
302
|
-
);
|
|
303
|
-
expect(snippet("invisible-image-explicit.html")).toContain("execute()");
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
it("returns the html unchanged for a page outside the registry", () => {
|
|
307
|
-
const html = page();
|
|
308
|
-
|
|
309
|
-
expect(handler(html, ctx(`${cwd}/src/some-other-page.html`))).toBe(html);
|
|
310
|
-
expect(handler(html, {} as IndexHtmlTransformContext)).toBe(html);
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
it("is idempotent", () => {
|
|
314
|
-
const once = handler(page(), ctx(`${cwd}/src/index.html`));
|
|
315
|
-
|
|
316
|
-
expect(handler(once, ctx(`${cwd}/src/index.html`))).toBe(once);
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
it("leaves html without a body untouched", () => {
|
|
320
|
-
expect(handler(noBody, ctx(`${cwd}/src/index.html`))).toBe(noBody);
|
|
321
|
-
});
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
describe("statusLogInjector", () => {
|
|
325
|
-
const handler = getHandler(statusLogInjector());
|
|
326
|
-
|
|
327
|
-
it("injects the status log container and js", () => {
|
|
328
|
-
const html = handler(page(), ctx("src/index.html"));
|
|
329
|
-
|
|
330
|
-
expect(html).toContain('id="captcha-status"');
|
|
331
|
-
expect(html).toContain("updateCaptchaStatus");
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
it("defines updateCaptchaStatus before the page's entry module runs", () => {
|
|
335
|
-
// What Vite hands a "post" transform: the entry module already hoisted
|
|
336
|
-
// into <head>. Module scripts run in document order, so the helper must
|
|
337
|
-
// be defined by a classic script ahead of it.
|
|
338
|
-
const built =
|
|
339
|
-
'<html><head><title>demo</title><script type="module" crossorigin src="/assets/entry.js"></script></head><body><p>hi</p></body></html>';
|
|
340
|
-
const html = handler(built, ctx("src/frictionless-explicit.html"));
|
|
341
|
-
|
|
342
|
-
const definedAt = html.indexOf("window.updateCaptchaStatus = ");
|
|
343
|
-
const entryAt = html.indexOf('src="/assets/entry.js"');
|
|
344
|
-
expect(definedAt).toBeGreaterThan(-1);
|
|
345
|
-
expect(definedAt).toBeLessThan(entryAt);
|
|
346
|
-
const definingScript = html.lastIndexOf("<script", definedAt);
|
|
347
|
-
expect(html.slice(definingScript, definedAt)).not.toContain("module");
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
it("wraps the page callbacks after the page's entry module", () => {
|
|
351
|
-
const built =
|
|
352
|
-
'<html><head><script type="module" src="/assets/entry.js"></script></head><body></body></html>';
|
|
353
|
-
const html = handler(built, ctx("src/image-explicit.html"));
|
|
354
|
-
|
|
355
|
-
expect(html.indexOf("window.onCaptchaFailed = ")).toBeGreaterThan(
|
|
356
|
-
html.indexOf('src="/assets/entry.js"'),
|
|
357
|
-
);
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
it("fills the events slot when the layout provides one", () => {
|
|
361
|
-
const html = handler(
|
|
362
|
-
page(`<form></form>${slotMarkup("events")}`),
|
|
363
|
-
ctx("src/index.html"),
|
|
364
|
-
);
|
|
365
|
-
|
|
366
|
-
expect(html).not.toContain(slotMarkup("events"));
|
|
367
|
-
expect(html.indexOf('id="captcha-status"')).toBeGreaterThan(
|
|
368
|
-
html.indexOf("</form>"),
|
|
369
|
-
);
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
it("places the status log directly after a form when there is no slot", () => {
|
|
373
|
-
const html = handler(
|
|
374
|
-
page("<form><input name='a'/></form><p>after</p>"),
|
|
375
|
-
ctx("src/index.html"),
|
|
376
|
-
);
|
|
377
|
-
|
|
378
|
-
expect(html.indexOf('id="captcha-status"')).toBeGreaterThan(
|
|
379
|
-
html.indexOf("</form>"),
|
|
380
|
-
);
|
|
381
|
-
expect(html.indexOf('id="captcha-status"')).toBeLessThan(
|
|
382
|
-
html.indexOf("<p>after</p>"),
|
|
383
|
-
);
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
it("falls back to the end of the body when there is no form", () => {
|
|
387
|
-
const html = handler(page(), ctx("src/index.html"));
|
|
388
|
-
|
|
389
|
-
expect(html.indexOf('id="captcha-status"')).toBeLessThan(
|
|
390
|
-
html.indexOf("</body>"),
|
|
391
|
-
);
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
it("is idempotent — a page that already has a status log is untouched", () => {
|
|
395
|
-
const once = handler(page(), ctx("src/index.html"));
|
|
396
|
-
|
|
397
|
-
expect(handler(once, ctx("src/index.html"))).toBe(once);
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
it("leaves html without a body untouched", () => {
|
|
401
|
-
expect(handler(noBody, ctx("src/index.html"))).toBe(noBody);
|
|
402
|
-
});
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
describe("placementInjector", () => {
|
|
406
|
-
const handler = getHandler(placementInjector());
|
|
407
|
-
|
|
408
|
-
it("fills the placement slot with both options and the script", () => {
|
|
409
|
-
const html = handler(page(slotMarkup("placement")), ctx("src/index.html"));
|
|
410
|
-
|
|
411
|
-
expect(html).not.toContain(slotMarkup("placement"));
|
|
412
|
-
expect(html).toContain('data-placement-option="popup"');
|
|
413
|
-
expect(html).toContain('data-placement-option="float"');
|
|
414
|
-
expect(html).toContain("data-placement");
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
it("uses links rather than buttons for the options", () => {
|
|
418
|
-
const html = handler(page(slotMarkup("placement")), ctx("src/index.html"));
|
|
419
|
-
|
|
420
|
-
expect(html).not.toMatch(/<button[^>]*data-placement-option/);
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
it("goes above the status log when there is no slot", () => {
|
|
424
|
-
const html = handler(
|
|
425
|
-
page('<div id="captcha-status" class="captcha-status"></div>'),
|
|
426
|
-
ctx("src/index.html"),
|
|
427
|
-
);
|
|
428
|
-
|
|
429
|
-
expect(html.indexOf('id="placement-switcher"')).toBeLessThan(
|
|
430
|
-
html.indexOf('id="captcha-status"'),
|
|
431
|
-
);
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
it("is idempotent", () => {
|
|
435
|
-
const once = handler(page(), ctx("src/index.html"));
|
|
436
|
-
|
|
437
|
-
expect(handler(once, ctx("src/index.html"))).toBe(once);
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
it("leaves html without a body untouched", () => {
|
|
441
|
-
expect(handler(noBody, ctx("src/index.html"))).toBe(noBody);
|
|
442
|
-
});
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
describe("formFillerInjector", () => {
|
|
446
|
-
const handler = getHandler(formFillerInjector());
|
|
447
|
-
|
|
448
|
-
it("is registered as a post transform", () => {
|
|
449
|
-
expect(formFillerInjector().transformIndexHtml).toMatchObject({
|
|
450
|
-
order: "post",
|
|
451
|
-
});
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
it("injects the button and its script", () => {
|
|
455
|
-
const html = handler(page(), ctx("src/index.html"));
|
|
456
|
-
|
|
457
|
-
expect(html).toContain('id="form-filler-button"');
|
|
458
|
-
expect(html).toContain("fillFormWithDefaults");
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
it("puts the button in the toolbar slot when the layout provides one", () => {
|
|
462
|
-
const html = handler(
|
|
463
|
-
page(`<div class="demo-toolbar">${slotMarkup("toolbar")}</div>`),
|
|
464
|
-
ctx("src/index.html"),
|
|
465
|
-
);
|
|
466
|
-
|
|
467
|
-
expect(html).not.toContain(slotMarkup("toolbar"));
|
|
468
|
-
expect(html).toMatch(
|
|
469
|
-
/<div class="demo-toolbar">\s*<button id="form-filler-button"/,
|
|
470
|
-
);
|
|
471
|
-
});
|
|
472
|
-
|
|
473
|
-
it("keeps the injected markup inside the body", () => {
|
|
474
|
-
const html = handler(page(), ctx("src/index.html"));
|
|
475
|
-
|
|
476
|
-
expect(html.indexOf('id="form-filler-button"')).toBeLessThan(
|
|
477
|
-
html.indexOf("</body>"),
|
|
478
|
-
);
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
it("leaves html without a body untouched", () => {
|
|
482
|
-
expect(handler(noBody, ctx("src/index.html"))).toBe(noBody);
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
it("leaves html with an opening but no closing body untouched", () => {
|
|
486
|
-
const fragment = "<html><body><div>truncated</div>";
|
|
487
|
-
|
|
488
|
-
expect(handler(fragment, ctx("src/index.html"))).toBe(fragment);
|
|
489
|
-
});
|
|
490
|
-
});
|
package/tsconfig.cjs.json
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../tsconfig.cjs.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"lib": ["ES2021", "dom"],
|
|
5
|
-
"types": ["node"],
|
|
6
|
-
"noEmit": true,
|
|
7
|
-
"noEmitOnError": true,
|
|
8
|
-
"sourceMap": false
|
|
9
|
-
},
|
|
10
|
-
"include": ["**/*.ts"],
|
|
11
|
-
"references": [
|
|
12
|
-
{
|
|
13
|
-
"path": "../../dev/config/tsconfig.cjs.json"
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"path": "../../packages/dotenv"
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
"path": "../../packages/locale"
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
"path": "../../packages/types"
|
|
23
|
-
}
|
|
24
|
-
]
|
|
25
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../tsconfig.esm.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"lib": ["ES2021", "dom"],
|
|
5
|
-
"types": ["node"],
|
|
6
|
-
"noEmit": true,
|
|
7
|
-
"noEmitOnError": true,
|
|
8
|
-
"sourceMap": false
|
|
9
|
-
},
|
|
10
|
-
"include": [
|
|
11
|
-
"**/*.ts",
|
|
12
|
-
"src/**/*.ts",
|
|
13
|
-
"src/**/*.tsx",
|
|
14
|
-
"src/**/*.json",
|
|
15
|
-
"src/**/*.d.ts"
|
|
16
|
-
],
|
|
17
|
-
"references": [
|
|
18
|
-
{
|
|
19
|
-
"path": "../../dev/config/tsconfig.json"
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
"path": "../../packages/dotenv"
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
"path": "../../packages/locale"
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
"path": "../../packages/types"
|
|
29
|
-
}
|
|
30
|
-
]
|
|
31
|
-
}
|