diff --git a/website/index.html b/website/index.html index cfb16a2..cb633ea 100644 --- a/website/index.html +++ b/website/index.html @@ -7,6 +7,26 @@ name="description" content="Voily turns raw voice into refined text, right where your cursor is." /> + + + + + + + + + + + + + + Voily - Just speak. We'll handle the rest. diff --git a/website/public/og-image.png b/website/public/og-image.png new file mode 100644 index 0000000..0b6d333 Binary files /dev/null and b/website/public/og-image.png differ diff --git a/website/src/components/SeoMetadata.test.ts b/website/src/components/SeoMetadata.test.ts new file mode 100644 index 0000000..f41b561 --- /dev/null +++ b/website/src/components/SeoMetadata.test.ts @@ -0,0 +1,50 @@ +import { existsSync, readFileSync } from 'node:fs' +import { describe, expect, test } from 'bun:test' + +const indexHtml = readFileSync(new URL('../../index.html', import.meta.url), 'utf8') +const ogImageUrl = new URL('../../public/og-image.png', import.meta.url) +const siteUrl = 'https://voily.pages.dev/' +const socialImageUrl = `${siteUrl}og-image.png` + +function metaContent(property: string) { + const escapedProperty = property.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + const pattern = new RegExp( + ``, + ) + + return indexHtml.match(pattern)?.[1] +} + +function linkHref(rel: string) { + const escapedRel = rel.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + const pattern = new RegExp(``) + + return indexHtml.match(pattern)?.[1] +} + +function readPngDimensions(buffer: Buffer) { + return { + width: buffer.readUInt32BE(16), + height: buffer.readUInt32BE(20), + } +} + +describe('SEO metadata', () => { + test('exposes a social preview image for Open Graph and Twitter cards', () => { + expect(linkHref('canonical')).toBe(siteUrl) + expect(metaContent('og:url')).toBe(siteUrl) + expect(metaContent('og:image')).toBe(socialImageUrl) + expect(metaContent('og:image:secure_url')).toBe(socialImageUrl) + expect(metaContent('twitter:image')).toBe(socialImageUrl) + expect(metaContent('og:image:width')).toBe('1200') + expect(metaContent('og:image:height')).toBe('630') + }) + + test('ships the social preview image as a 1200x630 PNG source asset', () => { + expect(existsSync(ogImageUrl)).toBe(true) + + const buffer = readFileSync(ogImageUrl) + expect(buffer.subarray(1, 4).toString('ascii')).toBe('PNG') + expect(readPngDimensions(buffer)).toEqual({ width: 1200, height: 630 }) + }) +})