@lullabot/playwright-testing 0.1.0 → 1.0.1
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/bin/github-a11y-summary +2 -2
- package/bin/github-failure-summary +4 -4
- package/lib/accessibility-baseline-file.d.ts +6 -6
- package/lib/accessibility-baseline-file.js +29 -23
- package/lib/accessibility-baseline.js +5 -3
- package/lib/accessible-screenshot.d.ts +4 -4
- package/lib/accessible-screenshot.js +88 -76
- package/lib/focus.js +4 -1
- package/lib/frames.js +4 -4
- package/lib/github/a11y-summary.js +74 -79
- package/lib/github/attachments.d.ts +1 -1
- package/lib/github/attachments.js +36 -34
- package/lib/github/failure-summary.d.ts +4 -4
- package/lib/github/failure-summary.js +139 -107
- package/lib/github/index.d.ts +6 -6
- package/lib/github/report-paths.js +17 -9
- package/lib/hover.js +17 -17
- package/lib/images.js +15 -12
- package/lib/index.d.ts +13 -13
- package/lib/interaction-states.d.ts +2 -2
- package/lib/interaction-states.js +7 -7
- package/lib/mock/index.d.ts +1 -1
- package/lib/mock/youtube.d.ts +1 -1
- package/lib/mock/youtube.js +1 -1
- package/lib/pseudo-state.d.ts +2 -2
- package/lib/pseudo-state.js +15 -7
- package/lib/videos.js +21 -10
- package/lib/visualdiff.d.ts +4 -4
- package/lib/visualdiff.js +35 -27
- package/package.json +3 -3
- package/src/accessibility-baseline-file.test.ts +190 -154
- package/src/accessibility-baseline-file.ts +107 -83
- package/src/accessibility-baseline.test.ts +633 -433
- package/src/accessibility-baseline.ts +41 -21
- package/src/accessible-screenshot.test.ts +551 -423
- package/src/accessible-screenshot.ts +334 -245
- package/src/focus.test.ts +23 -23
- package/src/focus.ts +7 -2
- package/src/fonts.test.ts +13 -13
- package/src/fonts.ts +1 -1
- package/src/frames.test.ts +10 -8
- package/src/frames.ts +5 -5
- package/src/github/a11y-summary.test.ts +436 -332
- package/src/github/a11y-summary.ts +189 -179
- package/src/github/attachments.test.ts +228 -167
- package/src/github/attachments.ts +144 -121
- package/src/github/failure-summary.test.ts +659 -445
- package/src/github/failure-summary.ts +376 -274
- package/src/github/index.test.ts +14 -14
- package/src/github/index.ts +6 -6
- package/src/github/report-paths.test.ts +211 -156
- package/src/github/report-paths.ts +75 -61
- package/src/hover.test.ts +50 -46
- package/src/hover.ts +18 -18
- package/src/images.test.ts +251 -206
- package/src/images.ts +52 -43
- package/src/index.ts +13 -13
- package/src/interaction-states.test.ts +72 -42
- package/src/interaction-states.ts +43 -29
- package/src/mock/index.ts +1 -1
- package/src/mock/youtube.test.ts +26 -26
- package/src/mock/youtube.ts +4 -6
- package/src/pseudo-state.test.ts +58 -51
- package/src/pseudo-state.ts +32 -27
- package/src/videos.test.ts +559 -399
- package/src/videos.ts +68 -44
- package/src/visualdiff.test.ts +416 -315
- package/src/visualdiff.ts +140 -99
|
@@ -1,248 +1,309 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from
|
|
2
|
-
import * as fs from
|
|
3
|
-
import * as os from
|
|
4
|
-
import * as path from
|
|
5
|
-
import { AttachmentUploader, mimeTypeFor, FetchLike } from
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as os from "os";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import { AttachmentUploader, mimeTypeFor, FetchLike } from "./attachments";
|
|
6
6
|
|
|
7
|
-
let tmpDir: string
|
|
7
|
+
let tmpDir: string;
|
|
8
8
|
|
|
9
9
|
beforeEach(() => {
|
|
10
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(),
|
|
11
|
-
})
|
|
10
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "attachments-test-"));
|
|
11
|
+
});
|
|
12
12
|
|
|
13
13
|
afterEach(() => {
|
|
14
|
-
fs.rmSync(tmpDir, { recursive: true, force: true })
|
|
15
|
-
})
|
|
14
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
15
|
+
});
|
|
16
16
|
|
|
17
17
|
/** Write a file of a given size and return its path. */
|
|
18
18
|
function writeFile(name: string, bytes = 16): string {
|
|
19
|
-
const filePath = path.join(tmpDir, name)
|
|
20
|
-
fs.writeFileSync(filePath, Buffer.alloc(bytes, 1))
|
|
21
|
-
return filePath
|
|
19
|
+
const filePath = path.join(tmpDir, name);
|
|
20
|
+
fs.writeFileSync(filePath, Buffer.alloc(bytes, 1));
|
|
21
|
+
return filePath;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/** A fetch stub that records calls and replays canned responses. */
|
|
25
|
-
function stubFetch(
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
function stubFetch(
|
|
26
|
+
responses: Array<{ ok: boolean; status: number; body: string }>,
|
|
27
|
+
) {
|
|
28
|
+
const calls: string[] = [];
|
|
29
|
+
const headers: Array<Record<string, string>> = [];
|
|
28
30
|
const impl: FetchLike = async (url, init) => {
|
|
29
|
-
calls.push(url)
|
|
30
|
-
headers.push(init.headers)
|
|
31
|
-
const next = responses.shift() ?? {
|
|
31
|
+
calls.push(url);
|
|
32
|
+
headers.push(init.headers);
|
|
33
|
+
const next = responses.shift() ?? {
|
|
34
|
+
ok: true,
|
|
35
|
+
status: 201,
|
|
36
|
+
body: '{"url":"https://example.test/a"}',
|
|
37
|
+
};
|
|
32
38
|
return {
|
|
33
39
|
ok: next.ok,
|
|
34
40
|
status: next.status,
|
|
35
41
|
text: async () => next.body,
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return { impl, calls, headers }
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
return { impl, calls, headers };
|
|
39
45
|
}
|
|
40
46
|
|
|
41
|
-
describe(
|
|
42
|
-
it(
|
|
43
|
-
expect(mimeTypeFor(
|
|
44
|
-
expect(mimeTypeFor(
|
|
45
|
-
expect(mimeTypeFor(
|
|
46
|
-
expect(mimeTypeFor(
|
|
47
|
-
expect(mimeTypeFor(
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
it(
|
|
51
|
-
expect(mimeTypeFor(
|
|
52
|
-
})
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
describe(
|
|
56
|
-
it(
|
|
57
|
-
const uploader = new AttachmentUploader({ repositoryId:
|
|
58
|
-
expect(uploader.enabled).toBe(false)
|
|
59
|
-
expect(uploader.disabledReason).toMatch(/no upload token/)
|
|
60
|
-
expect(await uploader.upload(writeFile(
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
it(
|
|
64
|
-
const uploader = new AttachmentUploader({ token:
|
|
65
|
-
expect(uploader.enabled).toBe(false)
|
|
66
|
-
expect(uploader.disabledReason).toMatch(/repository ID/)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it(
|
|
47
|
+
describe("mimeTypeFor", () => {
|
|
48
|
+
it("maps the image types GitHub renders inline", () => {
|
|
49
|
+
expect(mimeTypeFor("a/b/diff.png")).toBe("image/png");
|
|
50
|
+
expect(mimeTypeFor("shot.JPEG")).toBe("image/jpeg");
|
|
51
|
+
expect(mimeTypeFor("clip.webm")).toBe("video/webm");
|
|
52
|
+
expect(mimeTypeFor("icon.svg")).toBe("image/svg+xml");
|
|
53
|
+
expect(mimeTypeFor("clip.MOV")).toBe("video/quicktime");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("falls back to a generic type for anything else", () => {
|
|
57
|
+
expect(mimeTypeFor("report.txt")).toBe("application/octet-stream");
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("AttachmentUploader", () => {
|
|
62
|
+
it("is off without a token, which is the normal case", async () => {
|
|
63
|
+
const uploader = new AttachmentUploader({ repositoryId: "1" });
|
|
64
|
+
expect(uploader.enabled).toBe(false);
|
|
65
|
+
expect(uploader.disabledReason).toMatch(/no upload token/);
|
|
66
|
+
expect(await uploader.upload(writeFile("a.png"))).toBeNull();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("is off without a repository ID", () => {
|
|
70
|
+
const uploader = new AttachmentUploader({ token: "t" });
|
|
71
|
+
expect(uploader.enabled).toBe(false);
|
|
72
|
+
expect(uploader.disabledReason).toMatch(/repository ID/);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("returns the asset URL and passes the endpoint what it needs", async () => {
|
|
70
76
|
const { impl, calls } = stubFetch([
|
|
71
|
-
{
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
{
|
|
78
|
+
ok: true,
|
|
79
|
+
status: 201,
|
|
80
|
+
body: '{"url":"https://github.com/user-attachments/assets/abc"}',
|
|
81
|
+
},
|
|
82
|
+
]);
|
|
83
|
+
const uploader = new AttachmentUploader({
|
|
84
|
+
token: "t",
|
|
85
|
+
repositoryId: "42",
|
|
86
|
+
fetchImpl: impl,
|
|
87
|
+
});
|
|
74
88
|
|
|
75
|
-
const url = await uploader.upload(writeFile(
|
|
89
|
+
const url = await uploader.upload(writeFile("diff.png"));
|
|
76
90
|
|
|
77
|
-
expect(url).toBe(
|
|
78
|
-
expect(calls[0]).toContain(
|
|
79
|
-
expect(calls[0]).toContain(
|
|
80
|
-
expect(calls[0]).toContain(
|
|
81
|
-
expect(uploader.getStats().uploaded).toBe(1)
|
|
82
|
-
})
|
|
91
|
+
expect(url).toBe("https://github.com/user-attachments/assets/abc");
|
|
92
|
+
expect(calls[0]).toContain("repository_id=42");
|
|
93
|
+
expect(calls[0]).toContain("content_type=image%2Fpng");
|
|
94
|
+
expect(calls[0]).toContain("name=diff.png");
|
|
95
|
+
expect(uploader.getStats().uploaded).toBe(1);
|
|
96
|
+
});
|
|
83
97
|
|
|
84
|
-
it(
|
|
98
|
+
it("sends a Content-Type, which the endpoint rejects the request without", async () => {
|
|
85
99
|
// Node's fetch sends no Content-Type for a Buffer body, and the endpoint
|
|
86
100
|
// answers 400 "Invalid Content-Type" when it is missing.
|
|
87
|
-
const { impl, headers } = stubFetch([])
|
|
88
|
-
const uploader = new AttachmentUploader({
|
|
101
|
+
const { impl, headers } = stubFetch([]);
|
|
102
|
+
const uploader = new AttachmentUploader({
|
|
103
|
+
token: "t",
|
|
104
|
+
repositoryId: "1",
|
|
105
|
+
fetchImpl: impl,
|
|
106
|
+
});
|
|
89
107
|
|
|
90
|
-
await uploader.upload(writeFile(
|
|
108
|
+
await uploader.upload(writeFile("diff.png"));
|
|
91
109
|
|
|
92
|
-
expect(headers[0][
|
|
93
|
-
})
|
|
110
|
+
expect(headers[0]["Content-Type"]).toBe("image/png");
|
|
111
|
+
});
|
|
94
112
|
|
|
95
|
-
it(
|
|
113
|
+
it("reports the response body when the endpoint refuses", async () => {
|
|
96
114
|
const { impl } = stubFetch([
|
|
97
115
|
{ ok: false, status: 400, body: '{"message":"Invalid Content-Type"}' },
|
|
98
|
-
])
|
|
99
|
-
const uploader = new AttachmentUploader({
|
|
116
|
+
]);
|
|
117
|
+
const uploader = new AttachmentUploader({
|
|
118
|
+
token: "t",
|
|
119
|
+
repositoryId: "1",
|
|
120
|
+
fetchImpl: impl,
|
|
121
|
+
});
|
|
100
122
|
|
|
101
|
-
await uploader.upload(writeFile(
|
|
123
|
+
await uploader.upload(writeFile("a.png"));
|
|
102
124
|
|
|
103
|
-
expect(uploader.disabledReason).toContain(
|
|
104
|
-
})
|
|
125
|
+
expect(uploader.disabledReason).toContain("Invalid Content-Type");
|
|
126
|
+
});
|
|
105
127
|
|
|
106
|
-
it(
|
|
107
|
-
const { impl } = stubFetch([
|
|
108
|
-
|
|
128
|
+
it("accepts href as well as url, since write-ups disagree", async () => {
|
|
129
|
+
const { impl } = stubFetch([
|
|
130
|
+
{ ok: true, status: 201, body: '{"href":"https://example.test/h"}' },
|
|
131
|
+
]);
|
|
132
|
+
const uploader = new AttachmentUploader({
|
|
133
|
+
token: "t",
|
|
134
|
+
repositoryId: "1",
|
|
135
|
+
fetchImpl: impl,
|
|
136
|
+
});
|
|
109
137
|
|
|
110
|
-
expect(await uploader.upload(writeFile(
|
|
111
|
-
|
|
138
|
+
expect(await uploader.upload(writeFile("a.png"))).toBe(
|
|
139
|
+
"https://example.test/h",
|
|
140
|
+
);
|
|
141
|
+
});
|
|
112
142
|
|
|
113
|
-
it(
|
|
114
|
-
const { impl, calls } = stubFetch([
|
|
115
|
-
|
|
143
|
+
it("stops for the rest of the run after one failure", async () => {
|
|
144
|
+
const { impl, calls } = stubFetch([
|
|
145
|
+
{ ok: false, status: 404, body: '{"message":"Not Found"}' },
|
|
146
|
+
]);
|
|
147
|
+
const uploader = new AttachmentUploader({
|
|
148
|
+
token: "t",
|
|
149
|
+
repositoryId: "1",
|
|
150
|
+
fetchImpl: impl,
|
|
151
|
+
});
|
|
116
152
|
|
|
117
|
-
expect(await uploader.upload(writeFile(
|
|
118
|
-
expect(uploader.enabled).toBe(false)
|
|
119
|
-
expect(uploader.disabledReason).toMatch(/HTTP 404/)
|
|
153
|
+
expect(await uploader.upload(writeFile("one.png"))).toBeNull();
|
|
154
|
+
expect(uploader.enabled).toBe(false);
|
|
155
|
+
expect(uploader.disabledReason).toMatch(/HTTP 404/);
|
|
120
156
|
|
|
121
157
|
// The second attempt must not reach the endpoint at all.
|
|
122
|
-
expect(await uploader.upload(writeFile(
|
|
123
|
-
expect(calls).toHaveLength(1)
|
|
124
|
-
})
|
|
158
|
+
expect(await uploader.upload(writeFile("two.png"))).toBeNull();
|
|
159
|
+
expect(calls).toHaveLength(1);
|
|
160
|
+
});
|
|
125
161
|
|
|
126
|
-
it(
|
|
127
|
-
const { impl } = stubFetch([{ ok: true, status: 200, body:
|
|
128
|
-
const uploader = new AttachmentUploader({
|
|
162
|
+
it("stops when the endpoint answers without a URL", async () => {
|
|
163
|
+
const { impl } = stubFetch([{ ok: true, status: 200, body: "not json" }]);
|
|
164
|
+
const uploader = new AttachmentUploader({
|
|
165
|
+
token: "t",
|
|
166
|
+
repositoryId: "1",
|
|
167
|
+
fetchImpl: impl,
|
|
168
|
+
});
|
|
129
169
|
|
|
130
|
-
expect(await uploader.upload(writeFile(
|
|
131
|
-
expect(uploader.disabledReason).toMatch(/no URL/)
|
|
132
|
-
})
|
|
170
|
+
expect(await uploader.upload(writeFile("a.png"))).toBeNull();
|
|
171
|
+
expect(uploader.disabledReason).toMatch(/no URL/);
|
|
172
|
+
});
|
|
133
173
|
|
|
134
|
-
it(
|
|
174
|
+
it("stops rather than throwing when fetch rejects", async () => {
|
|
135
175
|
const impl: FetchLike = async () => {
|
|
136
|
-
throw new Error(
|
|
137
|
-
}
|
|
138
|
-
const uploader = new AttachmentUploader({
|
|
176
|
+
throw new Error("socket hang up");
|
|
177
|
+
};
|
|
178
|
+
const uploader = new AttachmentUploader({
|
|
179
|
+
token: "t",
|
|
180
|
+
repositoryId: "1",
|
|
181
|
+
fetchImpl: impl,
|
|
182
|
+
});
|
|
139
183
|
|
|
140
|
-
expect(await uploader.upload(writeFile(
|
|
141
|
-
expect(uploader.disabledReason).toMatch(/socket hang up/)
|
|
142
|
-
})
|
|
184
|
+
expect(await uploader.upload(writeFile("a.png"))).toBeNull();
|
|
185
|
+
expect(uploader.disabledReason).toMatch(/socket hang up/);
|
|
186
|
+
});
|
|
143
187
|
|
|
144
|
-
it(
|
|
145
|
-
const { impl } = stubFetch([])
|
|
188
|
+
it("enforces the upload count budget and says so", async () => {
|
|
189
|
+
const { impl } = stubFetch([]);
|
|
146
190
|
const uploader = new AttachmentUploader({
|
|
147
|
-
token:
|
|
148
|
-
repositoryId:
|
|
191
|
+
token: "t",
|
|
192
|
+
repositoryId: "1",
|
|
149
193
|
maxUploads: 1,
|
|
150
194
|
fetchImpl: impl,
|
|
151
|
-
})
|
|
195
|
+
});
|
|
152
196
|
|
|
153
|
-
expect(await uploader.upload(writeFile(
|
|
154
|
-
expect(await uploader.upload(writeFile(
|
|
155
|
-
expect(uploader.disabledReason).toMatch(/upload limit of 1/)
|
|
156
|
-
})
|
|
197
|
+
expect(await uploader.upload(writeFile("one.png"))).not.toBeNull();
|
|
198
|
+
expect(await uploader.upload(writeFile("two.png"))).toBeNull();
|
|
199
|
+
expect(uploader.disabledReason).toMatch(/upload limit of 1/);
|
|
200
|
+
});
|
|
157
201
|
|
|
158
|
-
it(
|
|
202
|
+
it("skips a file over GitHub's size limit and keeps going", async () => {
|
|
159
203
|
// GitHub refuses an image over 10 MB, and a rejection would otherwise
|
|
160
204
|
// switch uploading off for every image behind it.
|
|
161
|
-
const { impl, calls } = stubFetch([])
|
|
205
|
+
const { impl, calls } = stubFetch([]);
|
|
162
206
|
const uploader = new AttachmentUploader({
|
|
163
|
-
token:
|
|
164
|
-
repositoryId:
|
|
207
|
+
token: "t",
|
|
208
|
+
repositoryId: "1",
|
|
165
209
|
maxFileBytes: 100,
|
|
166
210
|
fetchImpl: impl,
|
|
167
|
-
})
|
|
211
|
+
});
|
|
168
212
|
|
|
169
|
-
expect(await uploader.upload(writeFile(
|
|
170
|
-
expect(uploader.enabled).toBe(true)
|
|
171
|
-
expect(uploader.lastSkipReason).toBe(
|
|
213
|
+
expect(await uploader.upload(writeFile("huge.png", 200))).toBeNull();
|
|
214
|
+
expect(uploader.enabled).toBe(true);
|
|
215
|
+
expect(uploader.lastSkipReason).toBe("too-large");
|
|
172
216
|
// Nothing was sent for it: the limit is checked before the request.
|
|
173
|
-
expect(calls).toHaveLength(0)
|
|
217
|
+
expect(calls).toHaveLength(0);
|
|
174
218
|
|
|
175
|
-
expect(await uploader.upload(writeFile(
|
|
176
|
-
expect(uploader.lastSkipReason).toBeNull()
|
|
177
|
-
expect(uploader.getStats()).toEqual({ uploaded: 1, skipped: 1, bytes: 10 })
|
|
178
|
-
})
|
|
219
|
+
expect(await uploader.upload(writeFile("small.png", 10))).not.toBeNull();
|
|
220
|
+
expect(uploader.lastSkipReason).toBeNull();
|
|
221
|
+
expect(uploader.getStats()).toEqual({ uploaded: 1, skipped: 1, bytes: 10 });
|
|
222
|
+
});
|
|
179
223
|
|
|
180
|
-
it(
|
|
181
|
-
const { impl } = stubFetch([])
|
|
224
|
+
it("skips a file that does not fit the byte budget, rather than stopping", async () => {
|
|
225
|
+
const { impl } = stubFetch([]);
|
|
182
226
|
const uploader = new AttachmentUploader({
|
|
183
|
-
token:
|
|
184
|
-
repositoryId:
|
|
227
|
+
token: "t",
|
|
228
|
+
repositoryId: "1",
|
|
185
229
|
maxTotalBytes: 100,
|
|
186
230
|
fetchImpl: impl,
|
|
187
|
-
})
|
|
231
|
+
});
|
|
188
232
|
|
|
189
|
-
expect(await uploader.upload(writeFile(
|
|
190
|
-
expect(await uploader.upload(writeFile(
|
|
191
|
-
expect(uploader.lastSkipReason).toBe(
|
|
233
|
+
expect(await uploader.upload(writeFile("first.png", 60))).not.toBeNull();
|
|
234
|
+
expect(await uploader.upload(writeFile("big.png", 60))).toBeNull();
|
|
235
|
+
expect(uploader.lastSkipReason).toBe("budget");
|
|
192
236
|
|
|
193
237
|
// The budget is a ceiling on the total, not a stop: what still fits goes.
|
|
194
|
-
expect(await uploader.upload(writeFile(
|
|
195
|
-
expect(uploader.enabled).toBe(true)
|
|
196
|
-
expect(uploader.getStats().bytes).toBe(90)
|
|
197
|
-
})
|
|
238
|
+
expect(await uploader.upload(writeFile("last.png", 30))).not.toBeNull();
|
|
239
|
+
expect(uploader.enabled).toBe(true);
|
|
240
|
+
expect(uploader.getStats().bytes).toBe(90);
|
|
241
|
+
});
|
|
198
242
|
|
|
199
|
-
it(
|
|
200
|
-
const { impl } = stubFetch([
|
|
243
|
+
it("tells an unreadable file apart from one the limits refused", async () => {
|
|
244
|
+
const { impl } = stubFetch([
|
|
245
|
+
{ ok: false, status: 404, body: '{"message":"Not Found"}' },
|
|
246
|
+
]);
|
|
201
247
|
const uploader = new AttachmentUploader({
|
|
202
|
-
token:
|
|
203
|
-
repositoryId:
|
|
248
|
+
token: "t",
|
|
249
|
+
repositoryId: "1",
|
|
204
250
|
maxFileBytes: 100,
|
|
205
251
|
fetchImpl: impl,
|
|
206
|
-
})
|
|
252
|
+
});
|
|
207
253
|
|
|
208
|
-
await uploader.upload(path.join(tmpDir,
|
|
209
|
-
expect(uploader.lastSkipReason).toBe(
|
|
254
|
+
await uploader.upload(path.join(tmpDir, "missing.png"));
|
|
255
|
+
expect(uploader.lastSkipReason).toBe("unreadable");
|
|
210
256
|
|
|
211
|
-
await uploader.upload(writeFile(
|
|
212
|
-
expect(uploader.lastSkipReason).toBe(
|
|
257
|
+
await uploader.upload(writeFile("huge.png", 200));
|
|
258
|
+
expect(uploader.lastSkipReason).toBe("too-large");
|
|
213
259
|
|
|
214
260
|
// A dead endpoint is the run's problem rather than any one file's.
|
|
215
|
-
await uploader.upload(writeFile(
|
|
216
|
-
expect(uploader.lastSkipReason).toBe(
|
|
217
|
-
expect(uploader.enabled).toBe(false)
|
|
218
|
-
})
|
|
261
|
+
await uploader.upload(writeFile("fine.png", 10));
|
|
262
|
+
expect(uploader.lastSkipReason).toBe("disabled");
|
|
263
|
+
expect(uploader.enabled).toBe(false);
|
|
264
|
+
});
|
|
219
265
|
|
|
220
|
-
it(
|
|
221
|
-
const { impl } = stubFetch([])
|
|
222
|
-
const uploader = new AttachmentUploader({
|
|
266
|
+
it("skips an unreadable file without giving up on the rest", async () => {
|
|
267
|
+
const { impl } = stubFetch([]);
|
|
268
|
+
const uploader = new AttachmentUploader({
|
|
269
|
+
token: "t",
|
|
270
|
+
repositoryId: "1",
|
|
271
|
+
fetchImpl: impl,
|
|
272
|
+
});
|
|
223
273
|
|
|
224
|
-
expect(await uploader.upload(path.join(tmpDir,
|
|
225
|
-
expect(uploader.enabled).toBe(true)
|
|
226
|
-
expect(await uploader.upload(writeFile(
|
|
227
|
-
})
|
|
274
|
+
expect(await uploader.upload(path.join(tmpDir, "missing.png"))).toBeNull();
|
|
275
|
+
expect(uploader.enabled).toBe(true);
|
|
276
|
+
expect(await uploader.upload(writeFile("present.png"))).not.toBeNull();
|
|
277
|
+
});
|
|
228
278
|
|
|
229
|
-
it(
|
|
279
|
+
it("reduces a name of nothing but dashes to a usable one", async () => {
|
|
230
280
|
// Trimming these with /^-+|-+$/ backtracks quadratically, so the trim is an
|
|
231
281
|
// index scan instead. Long runs of dashes are the shape that provokes it.
|
|
232
|
-
const { impl, calls } = stubFetch([])
|
|
233
|
-
const uploader = new AttachmentUploader({
|
|
282
|
+
const { impl, calls } = stubFetch([]);
|
|
283
|
+
const uploader = new AttachmentUploader({
|
|
284
|
+
token: "t",
|
|
285
|
+
repositoryId: "1",
|
|
286
|
+
fetchImpl: impl,
|
|
287
|
+
});
|
|
234
288
|
|
|
235
|
-
await uploader.upload(writeFile(
|
|
289
|
+
await uploader.upload(writeFile("a.png"), `${"-".repeat(50000)}b.png`);
|
|
236
290
|
|
|
237
|
-
expect(calls[0]).toContain(
|
|
238
|
-
})
|
|
291
|
+
expect(calls[0]).toContain("name=attachment");
|
|
292
|
+
});
|
|
239
293
|
|
|
240
|
-
it(
|
|
241
|
-
const { impl, calls } = stubFetch([])
|
|
242
|
-
const uploader = new AttachmentUploader({
|
|
294
|
+
it("reduces awkward file names to something a query string can carry", async () => {
|
|
295
|
+
const { impl, calls } = stubFetch([]);
|
|
296
|
+
const uploader = new AttachmentUploader({
|
|
297
|
+
token: "t",
|
|
298
|
+
repositoryId: "1",
|
|
299
|
+
fetchImpl: impl,
|
|
300
|
+
});
|
|
243
301
|
|
|
244
|
-
await uploader.upload(
|
|
302
|
+
await uploader.upload(
|
|
303
|
+
writeFile("weird.png"),
|
|
304
|
+
'homepage renders "correctly" & fast.png',
|
|
305
|
+
);
|
|
245
306
|
|
|
246
|
-
expect(calls[0]).toContain(
|
|
247
|
-
})
|
|
248
|
-
})
|
|
307
|
+
expect(calls[0]).toContain("name=homepage-renders-correctly-fast.png");
|
|
308
|
+
});
|
|
309
|
+
});
|