diff --git a/.gitea/workflows/test.yaml b/.gitea/workflows/test.yaml
index 8e833fe..f9fd104 100644
--- a/.gitea/workflows/test.yaml
+++ b/.gitea/workflows/test.yaml
@@ -7,4 +7,4 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - run: npm install && npm run test
\ No newline at end of file
+ - run: npm install && npm run test && npx playwright test
diff --git a/app/install/InstallPrompts.tsx b/app/install/InstallPrompts.tsx
index 3760af9..de93024 100644
--- a/app/install/InstallPrompts.tsx
+++ b/app/install/InstallPrompts.tsx
@@ -4,6 +4,7 @@ import EnableNotifications from "./EnableNotifications"
import OpenSafari from "./OpenSafari"
import InstallPWA from "./InstallPWA"
import Unsupported from "./Unsupported"
+import PermissionDenied from "./PermissionDenied"
interface InstallPromptsProps {
isMobileSafari: boolean
@@ -26,6 +27,7 @@ export default function InstallPrompts({
),
unsupported: ,
+ "permission denied": ,
}
return steps[step as keyof typeof steps] ?? null
diff --git a/app/install/PermissionDenied.tsx b/app/install/PermissionDenied.tsx
new file mode 100644
index 0000000..0ced448
--- /dev/null
+++ b/app/install/PermissionDenied.tsx
@@ -0,0 +1,13 @@
+import React from "react"
+
+export default function PermissionDenied() {
+ return (
+
+
+ Tack Up Now requires notifications permissions to work
+
+
+
Grant notifications permissions to use Tack Up Now
+
+ )
+}
diff --git a/app/useInstallState.ts b/app/useInstallState.ts
index 19f9961..5440f05 100644
--- a/app/useInstallState.ts
+++ b/app/useInstallState.ts
@@ -6,6 +6,7 @@ type IOSInstallStep =
| "open safari"
| "enable notifications"
| "unsupported"
+ | "permission denied"
export default function useInstallState({
isSupported,
@@ -29,6 +30,9 @@ export default function useInstallState({
window.Notification.permission === "granted") ||
canSendPush
+ const permissionDenied =
+ "Notification" in window && window.Notification.permission === "denied"
+
const isRunningPWA =
("standalone" in navigator && (navigator.standalone as boolean)) ||
matchMedia("(dislay-mode: standalone)").matches
@@ -40,9 +44,11 @@ export default function useInstallState({
? "open safari"
: !isRunningPWA && isMobileSafari
? "install"
- : !notificationsEnabled
- ? "enable notifications"
- : (null as IOSInstallStep | null),
+ : permissionDenied
+ ? "permission denied"
+ : !notificationsEnabled
+ ? "enable notifications"
+ : (null as IOSInstallStep | null),
installed: notificationsEnabled,
}
}
diff --git a/e2e/install.spec.ts b/e2e/install.spec.ts
index b295ad8..81a7b93 100644
--- a/e2e/install.spec.ts
+++ b/e2e/install.spec.ts
@@ -95,13 +95,23 @@ describe("when user is on iOS", () => {
await requestPromise
const yourNotificationsHeading =
- await page.getByText(/Your Notifications/)
+ page.getByText(/Your Notifications/)
await expect(yourNotificationsHeading).toBeVisible()
})
})
})
+ test("and notifications have been denied", async ({ page }) => {
+ await stubNotifications(page, { permission: "denied" })
+
+ await page.goto("/")
+
+ const deniedText = page.getByText(/requires notifications permissions/)
+
+ await expect(deniedText).toBeAttached()
+ })
+
describe("and notifications are enabled", () => {
beforeEach(async ({ page }) => {
await stubNotifications(page, { permission: "granted" })
@@ -111,7 +121,7 @@ describe("when user is on iOS", () => {
await page.goto("/")
await page.evaluate(async () => await navigator.serviceWorker.ready)
- const notificationText = await page.getByText(/Enable Notifications/)
+ const notificationText = page.getByText(/Enable Notifications/)
await expect(notificationText).not.toBeAttached()
})
@@ -119,8 +129,7 @@ describe("when user is on iOS", () => {
test("users see tack up now", async ({ page }) => {
await page.goto("/")
- const yourNotificationsHeading =
- await page.getByText(/Your Notifications/)
+ const yourNotificationsHeading = page.getByText(/Your Notifications/)
await expect(yourNotificationsHeading).toBeVisible()
})
@@ -139,13 +148,21 @@ describe("when user is on iOS", () => {
}) => {
await page.goto("/")
- const sorryText = await page.getByText(/Sorry/)
+ const sorryText = page.getByText(/Sorry/)
await expect(sorryText).toBeVisible()
})
})
})
+describe("other browsers", () => {
+ describe("when notifications permissions are unknown", () => {
+ test("prompt the user to allow notifications", async ({ page }) => {
+ // await page.
+ })
+ })
+})
+
function stubNotifications(
page: Page,
args: {
diff --git a/vite.config.ts b/vite.config.ts
index 3715143..a7ebb32 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -7,5 +7,9 @@ import { remixPWA } from "@remix-pwa/dev"
installGlobals()
export default defineConfig({
- plugins: [remix(), tsconfigPaths(), remixPWA()],
+ plugins: [
+ remix({ ignoredRouteFiles: ["**/*.test.*"] }),
+ tsconfigPaths(),
+ remixPWA(),
+ ],
})