图片来源:canopas
激动人心的消息!我们的博客有了一个新的新家!🚀
假设你想要将动态字体添加到你的网站。这里所说的动态字体指的是,它们应该根据条件加载,或者可以通过 API 响应获取。你不能直接通过 @font-face CSS 选择器来添加它们。
因此,在这种情形下,CSS Font Loading API 会很有用,使用 FontFace 加载和管理你的网页应用中的自定义字体。
在这篇博客文章中,我们将探讨如何在 typescript 中加载自定义字体以及编写Jest测试,以测试这些操作。
告别不切实际的幻想,开始有实际的愿望,从Justly开始,让你的愿望更加实际。
字体有两个主要属性,字体家族(例如 Roboto)和 样式(例如 Bold 或 Light)以及相关的文件。如下可能就是字体的结构,
字体类型定义如下: Font 类型: { family: 字体族; style: 样式; file: 文件; } 等
假设你有一个如下所示的 fonts
数组
const 字体数组: 字体[] = [ { 字体族: 'Roboto', 样式: '常规', 文件: 'Roboto-Regular.ttf', }, { 字体族: 'Roboto', 样式: '粗体', 文件: 'Roboto-Bold.ttf', }, ]
在处理字体时,有用的实体元素,
我们可以这么用它们,
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
。
就这样,今天的分享到此为止。继续探索,更多惊喜等你发现!!
开启Go语言测试之旅
感谢有你和我们一起经历这段旅程!
作为作者,如果你喜欢你所读的内容,一定要点个赞👏👏👏,这对我来说意义非凡!
请在下方的评论区里留下您的想法。您的意见让我们的内容更加丰富,激励我们为您带来更多有价值和有信息量的文章。
关注Canopas以了解有趣文章的更新!