From c1cfdb78de83714a584553b029616114c11bde9c Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 25 Sep 2024 14:09:30 -0400 Subject: [PATCH] Add installation instructions for not iOS --- .gitea/workflows/test.yaml | 2 +- .prettierrc | 2 +- app/install/EnableNotifications.tsx | 1 + app/install/InstallPrompts.tsx | 4 +- app/install/PermissionDenied.tsx | 9 ++++- app/routes/_index.test.tsx | 6 ++- app/routes/_index.tsx | 26 ++++++------ app/useInstallState.ts | 31 ++++++++++----- e2e/install.spec.ts | 62 +++++++++++++++++++++++++++-- 9 files changed, 109 insertions(+), 34 deletions(-) diff --git a/.gitea/workflows/test.yaml b/.gitea/workflows/test.yaml index f9fd104..acc0383 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 && npx playwright test + - run: npm install && npm run test && npx playwright install && npx playwright test diff --git a/.prettierrc b/.prettierrc index f010f27..a9d9104 100644 --- a/.prettierrc +++ b/.prettierrc @@ -13,7 +13,7 @@ "*.spec.js" ], "options": { - "maxLineLength": 9999999 + "maxLineLength": 9999 } } ] diff --git a/app/install/EnableNotifications.tsx b/app/install/EnableNotifications.tsx index aa5a09a..3527d3f 100644 --- a/app/install/EnableNotifications.tsx +++ b/app/install/EnableNotifications.tsx @@ -1,6 +1,7 @@ import React from "react" import { useEffect } from "react" import { usePush } from "../usePush" +import { request } from "http" export default function EnableNotifications({ onSubscribe, diff --git a/app/install/InstallPrompts.tsx b/app/install/InstallPrompts.tsx index de93024..6077ca0 100644 --- a/app/install/InstallPrompts.tsx +++ b/app/install/InstallPrompts.tsx @@ -9,6 +9,7 @@ import PermissionDenied from "./PermissionDenied" interface InstallPromptsProps { isMobileSafari: boolean isSupported: boolean + isIOS: boolean notificationsEnabled: boolean onInstallComplete: () => void } @@ -16,9 +17,10 @@ interface InstallPromptsProps { export default function InstallPrompts({ isSupported, isMobileSafari, + isIOS, onInstallComplete, }: InstallPromptsProps) { - const { step } = useInstallState({ isSupported, isMobileSafari }) + const { step } = useInstallState({ isSupported, isMobileSafari, isIOS }) const steps = { "open safari": , diff --git a/app/install/PermissionDenied.tsx b/app/install/PermissionDenied.tsx index 0ced448..8eda585 100644 --- a/app/install/PermissionDenied.tsx +++ b/app/install/PermissionDenied.tsx @@ -3,11 +3,16 @@ import React from "react" export default function PermissionDenied() { return (
+

Enable Notifications

- Tack Up Now requires notifications permissions to work + Tack Up Now requires notifications + permissions to work

-
Grant notifications permissions to use Tack Up Now
+
+ You have denied permission to send notifications, please grant + notifications permissions to use Tack Up Now +
) } diff --git a/app/routes/_index.test.tsx b/app/routes/_index.test.tsx index edb7944..a9ed4f7 100644 --- a/app/routes/_index.test.tsx +++ b/app/routes/_index.test.tsx @@ -14,7 +14,11 @@ describe("root", () => { path: "/", meta: () => [], links: () => [], - loader: () => ({ isSupported: true, isMobileSafari: true }), + loader: () => ({ + isSupported: true, + isMobileSafari: true, + isIOS: true, + }), Component: Index, }, ]) diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx index 439eb00..39f7b45 100644 --- a/app/routes/_index.tsx +++ b/app/routes/_index.tsx @@ -28,28 +28,25 @@ export const loader = async ({ request }: LoaderFunctionArgs) => { os.name !== "iOS" || versionAtLeast(coerceSemver(os.version) ?? "0.0.0", "16.4.0") + const isIOS = os.name === "iOS" + return json({ isSupported, isMobileSafari, - name: os.name, - version: os.version, + isIOS, }) } export default function Index() { - const { isSupported, isMobileSafari, name, version } = - useLoaderData() - - console.log(name, version) + const { isSupported, isMobileSafari, isIOS } = useLoaderData() return (
- - - +
) } @@ -57,11 +54,13 @@ export default function Index() { function LandingMessage({ isSupported, isMobileSafari, + isIOS, }: { isSupported: boolean isMobileSafari: boolean + isIOS: boolean }) { - const { installed } = useInstallState({ isSupported, isMobileSafari }) + const { installed } = useInstallState({ isSupported, isMobileSafari, isIOS }) const [isInstalled, setIsInstalled] = useState(installed) @@ -74,6 +73,7 @@ function LandingMessage({ setIsInstalled(true)} /> diff --git a/app/useInstallState.ts b/app/useInstallState.ts index 5440f05..af88aaa 100644 --- a/app/useInstallState.ts +++ b/app/useInstallState.ts @@ -11,9 +11,11 @@ type IOSInstallStep = export default function useInstallState({ isSupported, isMobileSafari, + isIOS, }: { isSupported: boolean isMobileSafari: boolean + isIOS: boolean }) { const isClient = typeof window !== "undefined" @@ -37,18 +39,25 @@ export default function useInstallState({ ("standalone" in navigator && (navigator.standalone as boolean)) || matchMedia("(dislay-mode: standalone)").matches + const iOSStates = [ + state(!isMobileSafari, "open safari"), + state(!isRunningPWA, "install"), + ] + + const states = [ + state(!isSupported, "unsupported"), + ...(isIOS ? iOSStates : []), + state(permissionDenied, "permission denied"), + state(!notificationsEnabled, "enable notifications"), + ] + return { - step: !isSupported - ? "unsupported" - : !isMobileSafari - ? "open safari" - : !isRunningPWA && isMobileSafari - ? "install" - : permissionDenied - ? "permission denied" - : !notificationsEnabled - ? "enable notifications" - : (null as IOSInstallStep | null), + step: states.find(({ active }) => active)?.result ?? null, installed: notificationsEnabled, } } + +const state = (active: boolean, result: IOSInstallStep) => ({ + active, + result, +}) diff --git a/e2e/install.spec.ts b/e2e/install.spec.ts index 81a7b93..ba65380 100644 --- a/e2e/install.spec.ts +++ b/e2e/install.spec.ts @@ -8,6 +8,12 @@ function webkitOnly() { }) } +function nonWebkitOnly() { + beforeEach(async ({ browserName }) => { + if (browserName === "webkit") skip() + }) +} + describe("when user is on iOS", () => { webkitOnly() @@ -108,7 +114,6 @@ describe("when user is on iOS", () => { await page.goto("/") const deniedText = page.getByText(/requires notifications permissions/) - await expect(deniedText).toBeAttached() }) @@ -156,10 +161,59 @@ describe("when user is on iOS", () => { }) describe("other browsers", () => { - describe("when notifications permissions are unknown", () => { - test("prompt the user to allow notifications", async ({ page }) => { - // await page. + nonWebkitOnly() + + beforeEach(async ({ page }) => { + await page.route("/api/subscribe", async (route) => { + await route.fulfill() }) + await stubServiceWorker(page) + }) + + test("prompt the user to allow notifications", async ({ page }) => { + await stubNotifications(page, { permission: "default" }) + await page.goto("/") + + const notificationText = page.getByText(/Enable Notifications/) + + await expect(notificationText).toBeAttached() + }) + + test("show tack up now when permission is granted", async ({ page }) => { + await stubNotifications(page, { permission: "granted" }) + + await page.goto("/") + + const yourNotificationsHeading = page.getByText(/Your Notifications/) + await expect(yourNotificationsHeading).toBeVisible() + }) + + test("submit the push subscription on permission granted", async ({ + page, + }) => { + await stubNotifications(page, { + permission: "default", + requestPermissionResult: "granted", + }) + + await page.goto("/") + + const requestPromise = page.waitForRequest("/api/subscribe") + await page.getByText(/Enable Notifications/).click() + const request = await requestPromise + + await expect(request.postDataJSON()).toEqual({ hi: "subscription" }) + }) + + test("prompt to allow notifications if permission was denied", async ({ + page, + }) => { + await stubNotifications(page, { permission: "denied" }) + + await page.goto("/") + + const deniedText = page.getByText(/requires notifications permissions/) + await expect(deniedText).toBeAttached() }) })