图片来源:canopas
介绍激动人心的消息!我们的博客有了一个新的新家!🚀
假设你想要将动态字体添加到你的网站。这里所说的动态字体指的是,它们应该根据条件加载,或者可以通过 API 响应获取。你不能直接通过 @font-face CSS 选择器来添加它们。
因此,在这种情形下,CSS Font Loading API 会很有用,使用 FontFace 加载和管理你的网页应用中的自定义字体。
在这篇博客文章中,我们将探讨如何在 typescript 中加载自定义字体以及编写Jest测试,以测试这些操作。
赞助告别不切实际的幻想,开始有实际的愿望,从Justly开始,让你的愿望更加实际。
Justly: 习惯、目标、日记 建立一个让自己每天进步1%的计划。justly.life 使用字体加载API来加载自定义字体文件字体有两个主要属性,字体家族(例如 Roboto)和 样式(例如 Bold 或 Light)以及相关的文件。如下可能就是字体的结构,
字体类型定义如下:
Font 类型: {
family: 字体族;
style: 样式;
file: 文件;
} 等
假设你有一个如下所示的 fonts
数组
const 字体数组: 字体[] = [
{
字体族: 'Roboto',
样式: '常规',
文件: 'Roboto-Regular.ttf',
},
{
字体族: 'Roboto',
样式: '粗体',
文件: 'Roboto-Bold.ttf',
},
]
在处理字体时,有用的实体元素,
- FontFace 构造函数:用于在网页应用中初始化字体,相当于 @font-face。
- FontFaceSet 管理字体加载并查询下载进度。
- document.font: 浏览器中已加载的字体集合
我们可以这么用它们,
export const loadFonts = async (fonts: Font[]): Promise<FontFaceSet> => {
// 获取已加载的字体,避免重复加载
const existingFonts = new Set(
Array.from(document.fonts.values()).map(
(fontFace) => fontFace.family
)
);
// 将待加载的字体加入文档
fonts.forEach((font) => {
const name = `${font.family}-${font.style}`;
// 如果字体已存在,则返回
if (existingFonts.has(name)) return;
// 初始化字体
const fontFace = new FontFace(name, `url(${font.file})`);
document.fonts.add(fontFace); // 准备字体集合
});
// 返回字体集合的 Promise
return document.fonts.ready.then();
}
当文档中的字体加载完成后,FontFaceSet
的承诺将得到完成,不再需要进一步加载字体。
就这样
这是加载自定义字体最简单的方式。
字体测试
虽然通过API管理字体资源非常简单,但是确保通过测试确保字体的正确运行至关重要,因为在进行测试时,由于没有浏览器环境,这可能会导致错误。
让我们尝试写一个不使用浏览器模拟环境的单元 Jest 测试,
describe('loadFonts', () => {
it('不应该重复添加已经在文档中存在的字体', async () => {
await utils.loadFonts(fonts);
expect(document.fonts.add).not.toHaveBeenCalled();
});
it('应该加载新字体到文档中', async () => {
document.fonts.values = jest.fn(() => [] as any);
await utils.loadFonts(fonts);
expect(document.fonts.add).toHaveBeenCalled();
});
});
它出现了这样的错误。这里的 undefined 指的是 document.fonts。
TypeError: 无法读取 'values' 属性,因为它是 undefined
让我们模拟一下 document.fonts,因为在 jest 环境下用不到它们。先创建一个 FontFaceSet
对象,并给它添加需要的属性。
// 模拟的FontFaceSet 对象
const mockFontFaceSet = {
add: jest.fn(), // 用于向document.fonts 添加字体
ready: Promise.resolve(), // 用于管理字体的加载
values: jest.fn(() => [ // 返回现有的字体信息
{ family: 'Roboto-Regular' },
{ family: 'Roboto-Bold' }
])
};
然后定义一下 document.fonts 对象(或简称 document.fonts)。
Object.defineProperty(document, 'font', {
value: mockFontFaceSet,
});
现在,当运行测试时,当遇到 document.fonts 时,jest 将将其用作 document.fonts
,返回 mockFontFaceSet
。
重新写一下这些测试。
describe('加载字体', () => {
it('不应该向已经包含这些字体的文档中添加字体', async () => {
await utils.loadFonts(fonts);
expect(document.fonts.add).not.toHaveBeenCalled(); // 验证没有调用 add 方法
});
it('应该将新字体加载到文档中', async () => {
document.fonts.values = jest.fn(() => [] as any);
await utils.loadFonts(fonts);
expect(document.fonts.add).toHaveBeenCalled(); // 验证调用了 add 方法
});
});
我们将为第二个测试用例收到错误 ReferenceError: FontFace is not defined
,这是因为 FontFace 在没有浏览器的情况下也是不可用的。
这里是在 jest.setup.ts
文件中定义字体的方法。
(global as any).FontFace = class {
constructor(public family?: string, public source?: string) { }
};
这样一来,现在fontface
可以在jest测试环境中使用,并且具有与fontface
构造函数相同的字体加载API
的功能。
这篇博客文章最初发表在canopas.com。
结语若要阅读完整版本,请访问该博客这里。
浏览器环境在服务器或测试环境中不可用,因此我们需创建一个浏览器实例的复制,以保证顺利运行。
开个玩笑来说,我们可以定义自定义变量并模拟浏览器环境。你可以使用相同的方法,比如模拟其他浏览器特性,比如 location
或 navigator
。
就这样,今天的分享到此为止。继续探索,更多惊喜等你发现!!
相关文章 Vue 3 组件测试与 Jest 前端 Vue 组件测试canopas.com 使用Gin和MySQL进行测试驱动开发(TDD)的Go语言之旅开启Go语言测试之旅
感谢有你和我们一起经历这段旅程!
作为作者,如果你喜欢你所读的内容,一定要点个赞👏👏👏,这对我来说意义非凡!
请在下方的评论区里留下您的想法。您的意见让我们的内容更加丰富,激励我们为您带来更多有价值和有信息量的文章。
关注Canopas以了解有趣文章的更新!
共同学习,写下你的评论
评论加载中...
作者其他优质文章