@flighthq/font 0.3.0-next.1032.e2b99fc → 0.3.0-next.1162.e90e518
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fontResourceFrom.d.ts","sourceRoot":"","sources":["../src/fontResourceFrom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"fontResourceFrom.d.ts","sourceRoot":"","sources":["../src/fontResourceFrom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAWtE,wBAAsB,yBAAyB,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAS3G;AAED,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAIvF;AAED,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAMnG;AAED,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAY3G"}
|
package/dist/fontResourceFrom.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { inferFontFormatFromUrl } from './fontFormat';
|
|
2
2
|
import { getFontShorthand } from './fontShorthand';
|
|
3
|
+
// These loaders fill a caller-owned FontResource. Each assigns `out.face` only after its load
|
|
4
|
+
// resolves, which is the contract on failure: a rejected load leaves the resource on whatever face it
|
|
5
|
+
// already had rather than blanking it, so a caller that swallows the rejection keeps rendering with a
|
|
6
|
+
// working font instead of losing one. Assigning before the await would silently turn a failed reload
|
|
7
|
+
// into a broken resource.
|
|
3
8
|
export async function loadFontResourceFromBytes(out, bytes) {
|
|
4
9
|
const face = new FontFace(out.family, bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));
|
|
5
10
|
await face.load();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fontResourceFrom.js","sourceRoot":"","sources":["../src/fontResourceFrom.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,GAAiB,EAAE,KAAiB;IAClF,MAAM,IAAI,GAAG,IAAI,QAAQ,CACvB,GAAG,CAAC,MAAM,EACT,KAAK,CAAC,MAAsB,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,CAC3F,CAAC;IACF,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IAClB,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzB,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IAChB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,GAAiB;IAC9D,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACtE,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1C,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,GAAiB,EAAE,GAAW;IAC1E,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,GAAG,CAAC,CAAC;IACrD,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IAClB,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzB,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IAChB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,GAAiB,EAAE,OAAkB;IAClF,MAAM,GAAG,GAAG,OAAO;SAChB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE;QACvB,MAAM,GAAG,GAAG,MAAM,IAAI,sBAAsB,CAAC,GAAG,CAAC,CAAC;QAClD,OAAO,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,GAAG,aAAa,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,GAAG,GAAG,CAAC;IACvE,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3C,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IAClB,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzB,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IAChB,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
1
|
+
{"version":3,"file":"fontResourceFrom.js","sourceRoot":"","sources":["../src/fontResourceFrom.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD,8FAA8F;AAC9F,sGAAsG;AACtG,sGAAsG;AACtG,qGAAqG;AACrG,0BAA0B;AAE1B,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,GAAiB,EAAE,KAAiB;IAClF,MAAM,IAAI,GAAG,IAAI,QAAQ,CACvB,GAAG,CAAC,MAAM,EACT,KAAK,CAAC,MAAsB,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,CAC3F,CAAC;IACF,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IAClB,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzB,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IAChB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,GAAiB;IAC9D,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACtE,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1C,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,GAAiB,EAAE,GAAW;IAC1E,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,GAAG,CAAC,CAAC;IACrD,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IAClB,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzB,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IAChB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,GAAiB,EAAE,OAAkB;IAClF,MAAM,GAAG,GAAG,OAAO;SAChB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE;QACvB,MAAM,GAAG,GAAG,MAAM,IAAI,sBAAsB,CAAC,GAAG,CAAC,CAAC;QAClD,OAAO,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,GAAG,aAAa,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,GAAG,GAAG,CAAC;IACvE,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3C,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IAClB,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzB,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IAChB,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/font",
|
|
3
|
-
"version": "0.3.0-next.
|
|
3
|
+
"version": "0.3.0-next.1162.e90e518",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/flighthq/flight.git",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@flighthq/entity": "0.3.0-next.
|
|
40
|
-
"@flighthq/types": "0.3.0-next.
|
|
39
|
+
"@flighthq/entity": "0.3.0-next.1162.e90e518",
|
|
40
|
+
"@flighthq/types": "0.3.0-next.1162.e90e518"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"typescript": "^5.3.0"
|
|
@@ -6,86 +6,231 @@ import {
|
|
|
6
6
|
loadFontResourceFromUrls,
|
|
7
7
|
} from './fontResourceFrom';
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
interface FontFaceConstruction {
|
|
10
|
+
family: string;
|
|
11
|
+
source: string | ArrayBuffer;
|
|
12
|
+
instance: MockFontFace;
|
|
13
|
+
}
|
|
10
14
|
|
|
15
|
+
let constructions: FontFaceConstruction[];
|
|
16
|
+
let addMock: ReturnType<typeof vi.fn>;
|
|
17
|
+
let loadMock: ReturnType<typeof vi.fn>;
|
|
18
|
+
|
|
19
|
+
class MockFontFace {
|
|
20
|
+
load = vi.fn().mockResolvedValue(undefined);
|
|
21
|
+
constructor(family: string, source: string | ArrayBuffer) {
|
|
22
|
+
constructions.push({ family, source, instance: this });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Mirrors the harness in fontFrom.test.ts, including its restore discipline: the document.fonts
|
|
27
|
+
// descriptor is saved and put back per test rather than left overwritten, so under a shared
|
|
28
|
+
// (isolate:false) worker the patch cannot leak into a sibling file.
|
|
29
|
+
let originalFonts: PropertyDescriptor | undefined;
|
|
11
30
|
beforeEach(() => {
|
|
12
|
-
|
|
13
|
-
vi.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}),
|
|
18
|
-
);
|
|
31
|
+
constructions = [];
|
|
32
|
+
addMock = vi.fn();
|
|
33
|
+
loadMock = vi.fn().mockResolvedValue([]);
|
|
34
|
+
vi.stubGlobal('FontFace', MockFontFace);
|
|
35
|
+
originalFonts = Object.getOwnPropertyDescriptor(document, 'fonts');
|
|
19
36
|
Object.defineProperty(document, 'fonts', {
|
|
20
|
-
value: { add:
|
|
37
|
+
value: { add: addMock, load: loadMock },
|
|
21
38
|
configurable: true,
|
|
22
|
-
writable: true,
|
|
23
39
|
});
|
|
24
|
-
vi.spyOn(document.fonts, 'add').mockImplementation(() => document.fonts);
|
|
25
|
-
vi.spyOn(document.fonts, 'load').mockResolvedValue([mockFace as unknown as FontFace]);
|
|
26
40
|
});
|
|
27
41
|
|
|
28
42
|
afterEach(() => {
|
|
43
|
+
if (originalFonts) {
|
|
44
|
+
Object.defineProperty(document, 'fonts', originalFonts);
|
|
45
|
+
} else {
|
|
46
|
+
delete (document as unknown as { fonts?: unknown }).fonts;
|
|
47
|
+
}
|
|
29
48
|
vi.unstubAllGlobals();
|
|
30
49
|
vi.restoreAllMocks();
|
|
31
50
|
});
|
|
32
51
|
|
|
33
52
|
describe('loadFontResourceFromBytes', () => {
|
|
34
|
-
it('
|
|
53
|
+
it('constructs the face under the resource family, loads it, and registers it', async () => {
|
|
35
54
|
const resource = createFontResource('TestFont');
|
|
36
55
|
const result = await loadFontResourceFromBytes(resource, new Uint8Array(8));
|
|
37
56
|
expect(result).toBe(resource);
|
|
38
|
-
expect(
|
|
39
|
-
expect(
|
|
40
|
-
expect(
|
|
57
|
+
expect(constructions).toHaveLength(1);
|
|
58
|
+
expect(constructions[0].family).toBe('TestFont');
|
|
59
|
+
expect(constructions[0].instance.load).toHaveBeenCalledOnce();
|
|
60
|
+
expect(addMock).toHaveBeenCalledWith(constructions[0].instance);
|
|
61
|
+
expect(resource.face).toBe(constructions[0].instance);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('slices only the view bytes out of a larger backing buffer', async () => {
|
|
65
|
+
// Same slice arithmetic as loadFontFromBytes. Tested on both because they are separate
|
|
66
|
+
// implementations: without this, a regression in one of the two would go unnoticed.
|
|
67
|
+
const backing = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
|
|
68
|
+
await loadFontResourceFromBytes(createFontResource('TestFont'), backing.subarray(2, 6));
|
|
69
|
+
expect(new Uint8Array(constructions[0].source as ArrayBuffer)).toEqual(new Uint8Array([3, 4, 5, 6]));
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('propagates a load failure and does not register the face', async () => {
|
|
73
|
+
const resource = createFontResource('TestFont');
|
|
74
|
+
const failing = new MockFontFace('TestFont', new ArrayBuffer(0));
|
|
75
|
+
failing.load.mockRejectedValue(new Error('bad font'));
|
|
76
|
+
vi.stubGlobal(
|
|
77
|
+
'FontFace',
|
|
78
|
+
vi.fn(function () {
|
|
79
|
+
return failing;
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
await expect(loadFontResourceFromBytes(resource, new Uint8Array(4))).rejects.toThrow('bad font');
|
|
83
|
+
expect(addMock).not.toHaveBeenCalled();
|
|
84
|
+
expect(resource.face).toBeNull();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('leaves a previously loaded face attached when a reload fails', async () => {
|
|
88
|
+
// The contract this pins: `face` is assigned only after the load resolves, so a failed reload
|
|
89
|
+
// rejects and leaves the resource on the font it already had rather than blanking it. A caller
|
|
90
|
+
// that ignores the rejection keeps rendering with a working font instead of losing one.
|
|
91
|
+
const resource = createFontResource('TestFont');
|
|
92
|
+
await loadFontResourceFromBytes(resource, new Uint8Array(4));
|
|
93
|
+
const loaded = resource.face;
|
|
94
|
+
expect(loaded).not.toBeNull();
|
|
95
|
+
|
|
96
|
+
const failing = new MockFontFace('TestFont', new ArrayBuffer(0));
|
|
97
|
+
failing.load.mockRejectedValue(new Error('boom'));
|
|
98
|
+
vi.stubGlobal(
|
|
99
|
+
'FontFace',
|
|
100
|
+
vi.fn(function () {
|
|
101
|
+
return failing;
|
|
102
|
+
}),
|
|
103
|
+
);
|
|
104
|
+
await expect(loadFontResourceFromBytes(resource, new Uint8Array(4))).rejects.toThrow('boom');
|
|
105
|
+
expect(resource.face).toBe(loaded);
|
|
106
|
+
expect(addMock).toHaveBeenCalledTimes(1);
|
|
41
107
|
});
|
|
42
108
|
});
|
|
43
109
|
|
|
44
110
|
describe('loadFontResourceFromName', () => {
|
|
45
|
-
it('
|
|
111
|
+
it('queries the family via the escaped font shorthand', async () => {
|
|
112
|
+
await loadFontResourceFromName(createFontResource('MyFont'));
|
|
113
|
+
expect(loadMock).toHaveBeenCalledWith("1em 'MyFont'");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('escapes a single quote in the family name so the shorthand stays valid', async () => {
|
|
117
|
+
await loadFontResourceFromName(createFontResource("Josh's Font"));
|
|
118
|
+
expect(loadMock).toHaveBeenCalledWith("1em 'Josh\\'s Font'");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('attaches the first face when the document returns several', async () => {
|
|
122
|
+
const first = { family: 'first' } as unknown as FontFace;
|
|
123
|
+
const second = { family: 'second' } as unknown as FontFace;
|
|
124
|
+
loadMock.mockResolvedValue([first, second]);
|
|
46
125
|
const resource = createFontResource('TestFont');
|
|
47
126
|
const result = await loadFontResourceFromName(resource);
|
|
48
127
|
expect(result).toBe(resource);
|
|
49
|
-
expect(resource.face).toBe(
|
|
128
|
+
expect(resource.face).toBe(first);
|
|
50
129
|
});
|
|
51
130
|
|
|
52
131
|
it('leaves face null when no registered faces are found', async () => {
|
|
53
|
-
vi.spyOn(document.fonts, 'load').mockResolvedValue([]);
|
|
54
132
|
const resource = createFontResource('UnknownFont');
|
|
55
133
|
await loadFontResourceFromName(resource);
|
|
56
134
|
expect(resource.face).toBeNull();
|
|
57
135
|
});
|
|
136
|
+
|
|
137
|
+
it('registers nothing — a name lookup finds an existing face rather than adding one', async () => {
|
|
138
|
+
loadMock.mockResolvedValue([{ family: 'x' } as unknown as FontFace]);
|
|
139
|
+
await loadFontResourceFromName(createFontResource('TestFont'));
|
|
140
|
+
expect(addMock).not.toHaveBeenCalled();
|
|
141
|
+
expect(constructions).toHaveLength(0);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('propagates a lookup failure', async () => {
|
|
145
|
+
loadMock.mockRejectedValue(new Error('no such font'));
|
|
146
|
+
await expect(loadFontResourceFromName(createFontResource('MyFont'))).rejects.toThrow('no such font');
|
|
147
|
+
});
|
|
58
148
|
});
|
|
59
149
|
|
|
60
150
|
describe('loadFontResourceFromUrl', () => {
|
|
61
|
-
it('
|
|
62
|
-
const resource = createFontResource('
|
|
151
|
+
it('builds a url() source under the resource family and registers the face', async () => {
|
|
152
|
+
const resource = createFontResource('MyFont');
|
|
63
153
|
const result = await loadFontResourceFromUrl(resource, 'https://example.com/font.woff2');
|
|
64
154
|
expect(result).toBe(resource);
|
|
65
|
-
expect(
|
|
66
|
-
expect(
|
|
155
|
+
expect(constructions[0].family).toBe('MyFont');
|
|
156
|
+
expect(constructions[0].source).toBe('url(https://example.com/font.woff2)');
|
|
157
|
+
expect(addMock).toHaveBeenCalledWith(constructions[0].instance);
|
|
158
|
+
expect(resource.face).toBe(constructions[0].instance);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('adds no format() hint — a single url carries none, unlike the multi-source form', async () => {
|
|
162
|
+
await loadFontResourceFromUrl(createFontResource('MyFont'), 'font.woff2');
|
|
163
|
+
expect(constructions[0].source).toBe('url(font.woff2)');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('propagates a load failure and does not register the face', async () => {
|
|
167
|
+
const resource = createFontResource('MyFont');
|
|
168
|
+
const failing = new MockFontFace('MyFont', '');
|
|
169
|
+
failing.load.mockRejectedValue(new Error('bad url'));
|
|
170
|
+
vi.stubGlobal(
|
|
171
|
+
'FontFace',
|
|
172
|
+
vi.fn(function () {
|
|
173
|
+
return failing;
|
|
174
|
+
}),
|
|
175
|
+
);
|
|
176
|
+
await expect(loadFontResourceFromUrl(resource, 'font.woff2')).rejects.toThrow('bad url');
|
|
177
|
+
expect(addMock).not.toHaveBeenCalled();
|
|
178
|
+
expect(resource.face).toBeNull();
|
|
67
179
|
});
|
|
68
180
|
});
|
|
69
181
|
|
|
70
182
|
describe('loadFontResourceFromUrls', () => {
|
|
71
|
-
it('
|
|
72
|
-
|
|
183
|
+
it('composes a comma-joined src with explicit and inferred format() hints', async () => {
|
|
184
|
+
// The block this replaces was titled "builds a multi-source src string" and asserted nothing
|
|
185
|
+
// about the string — only that a face was attached. The composed src is the whole point of this
|
|
186
|
+
// function, and it was the one thing not checked.
|
|
187
|
+
const resource = createFontResource('MyFont');
|
|
73
188
|
const result = await loadFontResourceFromUrls(resource, [
|
|
74
189
|
{ url: 'font.woff2', format: 'woff2' },
|
|
75
190
|
{ url: 'font.ttf' },
|
|
76
191
|
]);
|
|
77
192
|
expect(result).toBe(resource);
|
|
78
|
-
expect(
|
|
79
|
-
expect(
|
|
193
|
+
expect(constructions[0].family).toBe('MyFont');
|
|
194
|
+
expect(constructions[0].source).toBe("url(font.woff2) format('woff2'), url(font.ttf) format('truetype')");
|
|
195
|
+
expect(addMock).toHaveBeenCalledWith(constructions[0].instance);
|
|
80
196
|
});
|
|
81
197
|
|
|
82
|
-
it('infers format from file extension when
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
await loadFontResourceFromUrls(
|
|
89
|
-
expect(
|
|
198
|
+
it('infers the format from the file extension when none is given', async () => {
|
|
199
|
+
await loadFontResourceFromUrls(createFontResource('MyFont'), [{ url: 'font.otf' }]);
|
|
200
|
+
expect(constructions[0].source).toBe("url(font.otf) format('opentype')");
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('omits the format() hint when it cannot be inferred and none is given', async () => {
|
|
204
|
+
await loadFontResourceFromUrls(createFontResource('MyFont'), [{ url: 'font.bin' }]);
|
|
205
|
+
expect(constructions[0].source).toBe('url(font.bin)');
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('mixes inferred, explicit, and absent hints across one src list', async () => {
|
|
209
|
+
await loadFontResourceFromUrls(createFontResource('MyFont'), [
|
|
210
|
+
{ url: 'a.woff2', format: 'woff2' },
|
|
211
|
+
{ url: 'b.ttf' },
|
|
212
|
+
{ url: 'c.bin' },
|
|
213
|
+
]);
|
|
214
|
+
expect(constructions[0].source).toBe("url(a.woff2) format('woff2'), url(b.ttf) format('truetype'), url(c.bin)");
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('builds an empty src for an empty source list rather than throwing', async () => {
|
|
218
|
+
await loadFontResourceFromUrls(createFontResource('MyFont'), []);
|
|
219
|
+
expect(constructions[0].source).toBe('');
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('propagates a load failure and does not register the face', async () => {
|
|
223
|
+
const resource = createFontResource('MyFont');
|
|
224
|
+
const failing = new MockFontFace('MyFont', '');
|
|
225
|
+
failing.load.mockRejectedValue(new Error('bad url'));
|
|
226
|
+
vi.stubGlobal(
|
|
227
|
+
'FontFace',
|
|
228
|
+
vi.fn(function () {
|
|
229
|
+
return failing;
|
|
230
|
+
}),
|
|
231
|
+
);
|
|
232
|
+
await expect(loadFontResourceFromUrls(resource, [{ url: 'font.woff2' }])).rejects.toThrow('bad url');
|
|
233
|
+
expect(addMock).not.toHaveBeenCalled();
|
|
234
|
+
expect(resource.face).toBeNull();
|
|
90
235
|
});
|
|
91
236
|
});
|