diff --git a/nginx/mime.types b/nginx/mime.types
index b7ff2b4..45bd9e0 100644
--- a/nginx/mime.types
+++ b/nginx/mime.types
@@ -29,6 +29,7 @@ types {
application/java-archive jar war ear;
application/json json;
+ application/manifest+json webmanifest;
application/mac-binhex40 hqx;
application/msword doc;
application/pdf pdf;
diff --git a/nginx/nginx.conf b/nginx/nginx.conf
index 3522f47..4654753 100644
--- a/nginx/nginx.conf
+++ b/nginx/nginx.conf
@@ -118,13 +118,13 @@ http {
location ~* \.(?:css|js|jpg|svg)$ {
gzip_static on;
- try_files $uri $uri/ $uri.html /iframe.html /index.html;
+ try_files $uri =404;
}
location ~* \.(?:json)$ {
gzip_static on;
- try_files $uri $uri/ $uri.html /iframe.html /index.html;
+ try_files $uri =404;
}
location ~* (?:#.*#|\.(?:bak|conf|dist|fla|in[ci]|log|orig|psd|sh|sql|sw[op])|~)$ {
diff --git a/nginx/tests/mime.types b/nginx/tests/mime.types
index b7ff2b4..45bd9e0 100644
--- a/nginx/tests/mime.types
+++ b/nginx/tests/mime.types
@@ -29,6 +29,7 @@ types {
application/java-archive jar war ear;
application/json json;
+ application/manifest+json webmanifest;
application/mac-binhex40 hqx;
application/msword doc;
application/pdf pdf;
diff --git a/nginx/tests/nginx.conf b/nginx/tests/nginx.conf
index 1749013..9c8d8e7 100644
--- a/nginx/tests/nginx.conf
+++ b/nginx/tests/nginx.conf
@@ -113,14 +113,14 @@ http {
location ~* \.(?:css|js|jpg|svg)$ {
gzip_static on;
- try_files $uri $uri/ $uri.html /iframe.html /index.html;
+ try_files $uri =404;
expires max;
}
location ~* \.(?:json)$ {
gzip_static on;
- try_files $uri $uri/ $uri.html /iframe.html /index.html;
+ try_files $uri =404;
expires max;
}
diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png
deleted file mode 100644
index f938b9b..0000000
Binary files a/public/favicon-16x16.png and /dev/null differ
diff --git a/public/favicon.ico b/public/favicon.ico
index e1c5350..e4290fb 100644
Binary files a/public/favicon.ico and b/public/favicon.ico differ
diff --git a/public/icon.svg b/public/icon.svg
new file mode 100644
index 0000000..303c7d0
--- /dev/null
+++ b/public/icon.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/app/_layout/metadata.tsx b/src/app/_layout/metadata.tsx
index c3f1316..2f9c008 100644
--- a/src/app/_layout/metadata.tsx
+++ b/src/app/_layout/metadata.tsx
@@ -75,15 +75,17 @@ const buildMetadata = (locale: Locale): Metadata => ({
creator: '@raferdev',
},
icons: {
- icon: '/favicon.ico',
- shortcut: '/favicon-16x16.png',
+ icon: [
+ { url: '/favicon.ico', sizes: '32x32' },
+ { url: '/icon.svg', type: 'image/svg+xml' },
+ ],
apple: '/apple-touch-icon.png',
},
robots: {
index: true,
follow: true,
},
- manifest: `${meta.url}/site.webmanifest`,
+ manifest: '/manifest.webmanifest',
})
const viewport: Viewport = {
diff --git a/tests/icons.spec.ts b/tests/icons.spec.ts
new file mode 100644
index 0000000..820470a
--- /dev/null
+++ b/tests/icons.spec.ts
@@ -0,0 +1,76 @@
+import { expect, test } from '@playwright/test'
+
+const PAGES = ['/', '/pt']
+
+test.describe('Icons and manifest', () => {
+ for (const path of PAGES) {
+ test(`Expected - Every icon and manifest linked from ${path} to load`, async ({
+ page,
+ request,
+ }) => {
+ await page.goto(path)
+
+ const links = await page
+ .locator(
+ 'head link[rel~="icon"], head link[rel="apple-touch-icon"], head link[rel="manifest"]'
+ )
+ .evaluateAll((elements) =>
+ elements.map((element) => ({
+ rel: element.getAttribute('rel'),
+ type: element.getAttribute('type'),
+ href: (element as HTMLLinkElement).href,
+ }))
+ )
+
+ expect(links.map(({ rel }) => rel)).toEqual(
+ expect.arrayContaining(['icon', 'apple-touch-icon', 'manifest'])
+ )
+
+ for (const { rel, type, href } of links) {
+ const response = await request.get(href)
+ const contentType = response.headers()['content-type']
+ expect(response.status(), `${rel} ${href}`).toBe(200)
+ if (rel !== 'manifest') expect(contentType, href).toMatch(/^image\//)
+ if (type) expect(contentType).toContain(type)
+
+ if (rel === 'manifest') {
+ expect(contentType).toContain('application/manifest+json')
+ const manifest = await response.json()
+ for (const icon of manifest.icons) {
+ const iconResponse = await request.get(new URL(icon.src, href).href)
+ expect(iconResponse.status(), icon.src).toBe(200)
+ expect(iconResponse.headers()['content-type'], icon.src).toMatch(
+ /^image\//
+ )
+ }
+ }
+ }
+ })
+ }
+
+ for (const asset of [
+ '/missing.svg',
+ '/missing.js',
+ '/missing.css',
+ '/missing.json',
+ ]) {
+ test(`Expected - A missing ${asset} to 404 instead of serving a page`, async ({
+ request,
+ }) => {
+ const response = await request.get(asset)
+
+ expect(response.status()).toBe(404)
+ })
+ }
+
+ test('Expected - The SVG icon to follow the color scheme', async ({
+ request,
+ }) => {
+ const response = await request.get('/icon.svg')
+ const svg = await response.text()
+
+ expect(response.headers()['content-type']).toContain('image/svg+xml')
+ expect(svg.startsWith('