Skip to content

Commit

Permalink
Upgrade fake-permissions
Browse files Browse the repository at this point in the history
  • Loading branch information
ezzatron committed Aug 8, 2024
1 parent a64221a commit 9d21bf8
Show file tree
Hide file tree
Showing 12 changed files with 153 additions and 111 deletions.
16 changes: 13 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ Versioning].

## Unreleased

### Changed

- **\[BREAKING]** This release includes an update to the version of
`fake-permissions` used for permissions handling, which includes many breaking
changes. See the [`fake-permissions` releases] for details.
- **\[BREAKING]** The `createGeolocation()` function no longer takes a `user`
option. Access requests are now handled by the `permissionStore` object.

[`fake-permissions` releases]: https://github.com/ezzatron/fake-permissions/releases

## [v0.12.0] - 2024-08-08

[v0.12.0]: https://github.com/ezzatron/fake-geolocation/releases/tag/v0.12.0
Expand Down Expand Up @@ -76,7 +86,7 @@ const coordsB = createCoordinates({ latitude: 3, longitude: 4 });

// Jump to some coords and grant permission
user.jumpToCoordinates(coordsA);
user.grantPermission({ name: "geolocation" });
user.grantAccess({ name: "geolocation" });

// Start watching the position
let position: GeolocationPosition | undefined;
Expand Down Expand Up @@ -136,14 +146,14 @@ console.log(error?.code === POSITION_UNAVAILABLE);

// Wait for a PERMISSION_DENIED error, while running a task
await observer.waitForPositionError(PERMISSION_DENIED, async () => {
user.denyPermission({ name: "geolocation" });
user.blockAccess({ name: "geolocation" });
});
// Outputs "true"
console.log(error?.code === PERMISSION_DENIED);

// You can also wait for geolocation permission states
await observer.waitForPermissionState("granted", async () => {
user.grantPermission({ name: "geolocation" });
user.grantAccess({ name: "geolocation" });
});
// Outputs "true"
console.log(status.state === "granted");
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"prepublishOnly": "tsc -p tsconfig.build.json"
},
"dependencies": {
"fake-permissions": "^0.10.0"
"fake-permissions": "^0.12.0"
},
"devDependencies": {
"@types/web": "^0.0.152",
Expand Down
10 changes: 2 additions & 8 deletions src/create-apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export function createAPIs({
} {
const locationServices = createLocationServices({ acquireDelay });
const permissions = createPermissions({ permissionStore });
const geolocation = createGeolocation({ locationServices, permissionStore });
const observer = createGeolocationObserver(geolocation, permissions);

const user = createUser({
handleAccessRequest,
Expand All @@ -45,14 +47,6 @@ export function createAPIs({
permissionStore,
});

const geolocation = createGeolocation({
locationServices,
permissionStore,
user,
});

const observer = createGeolocationObserver(geolocation, permissions);

return {
geolocation,
locationServices,
Expand Down
40 changes: 15 additions & 25 deletions src/geolocation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ import {
} from "./geolocation-position-error.js";
import { createPosition, isHighAccuracy } from "./geolocation-position.js";
import { LocationServices, Unsubscribe } from "./location-services.js";
import type { User } from "./user.js";

type GeolocationParameters = {
locationServices: LocationServices;
permissionStore: PermissionStore;
user: User;
};

const descriptor: PermissionDescriptor = { name: "geolocation" };
let canConstruct = false;

export function createGeolocation(
Expand All @@ -26,17 +25,12 @@ export function createGeolocation(
}

export class Geolocation {
constructor({
locationServices,
permissionStore,
user,
}: GeolocationParameters) {
constructor({ locationServices, permissionStore }: GeolocationParameters) {
if (!canConstruct) throw new TypeError("Illegal constructor");
canConstruct = false;

this.#locationServices = locationServices;
this.#permissionStore = permissionStore;
this.#user = user;
this.#cachedPosition = null;
this.#watchIds = [];
this.#watchUnsubscribers = {};
Expand Down Expand Up @@ -191,14 +185,12 @@ export class Geolocation {
* 5. Let descriptor be a new PermissionDescriptor whose name is
* "geolocation".
*/
const descriptor: PermissionDescriptor = {
name: "geolocation",
};
// descriptor is defined as a constant at the top of the file

/*
* 6. Set permission to request permission to use descriptor.
*/
const isAllowed = await this.#user.requestAccess(descriptor);
const isAllowed = await this.#permissionStore.requestAccess(descriptor);

/*
* 7. If permission is "denied", then:
Expand Down Expand Up @@ -275,12 +267,12 @@ export class Geolocation {
);

const unsubscribePermission = this.#permissionStore.subscribe(
(descriptor, toState) => {
(descriptor, { hasAccess, hadAccess }) => {
if (descriptor.name !== "geolocation") return;
if (hasAccess === hadAccess) return;

if (toState === "granted") {
// Produce a new position immediately when the permission changes to
// "granted".
if (hasAccess) {
// Produce a new position immediately when access is granted.
this.#acquirePosition(
successCallback,
errorCallback,
Expand All @@ -292,9 +284,9 @@ export class Geolocation {
/* v8 ignore stop */
);
} else {
// Produce PERMISSION_DENIED errors immediately when the permission
// changes to something other than "granted". This is not part of the
// spec, but Chrome does it, and it's useful for testing.
// Produce PERMISSION_DENIED errors immediately when access is
// revoked. This is not part of the spec, but Chrome does it, and it's
// useful for testing.
this.#invokeErrorCallback(
errorCallback,
createPermissionDeniedError(""),
Expand Down Expand Up @@ -372,13 +364,13 @@ export class Geolocation {
* 1. Let permission be get the current permission state of
* "geolocation".
*/
const permission = this.#permissionStore.get({ name: "geolocation" });
const hasAccess = this.#permissionStore.hasAccess(descriptor);

/*
* 5. (cont.)
* 2. If permission is "denied":
*/
if (permission === "denied") {
if (!hasAccess) {
/*
* 5. (cont.)
* 2. (cont.)
Expand Down Expand Up @@ -482,9 +474,8 @@ export class Geolocation {

new Promise<never>((_resolve, reject) => {
const unsubscribePermission = this.#permissionStore.subscribe(
(descriptor, toState) => {
if (descriptor.name !== "geolocation") return;
if (toState === "granted") return;
(descriptor, { hasAccess }) => {
if (descriptor.name !== "geolocation" || hasAccess) return;

// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
reject(GeolocationPositionError.PERMISSION_DENIED);
Expand Down Expand Up @@ -642,7 +633,6 @@ export class Geolocation {

#locationServices: LocationServices;
#permissionStore: PermissionStore;
#user: User;
#cachedPosition: GeolocationPosition | null;
#watchIds: number[];
#watchUnsubscribers: Record<number, Unsubscribe>;
Expand Down
2 changes: 1 addition & 1 deletion test/vitest/create-apis.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe("createAPIs()", () => {

await expect(
observer.waitForPermissionState("granted", async () => {
user.grantPermission({ name: "geolocation" });
user.grantAccess({ name: "geolocation" });
}),
).resolves.toBeUndefined();
expect((await permissions.query({ name: "geolocation" })).state).toBe(
Expand Down
29 changes: 20 additions & 9 deletions test/vitest/create-wrapped-apis.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
createWrappedAPIs,
type GeolocationObserver,
} from "fake-geolocation";
import type { PermissionStore } from "fake-permissions";
import {
afterEach,
beforeEach,
Expand All @@ -22,9 +23,11 @@ describe("createWrappedAPIs()", () => {
const startTime = 100;

let suppliedUser: User;
let suppliedPermissionStore: PermissionStore;

let geolocation: Geolocation;
let observer: GeolocationObserver;
let permissionStore: PermissionStore;
let permissions: Permissions;
let user: User;
let selectAPIs: (useSuppliedAPIs: boolean) => void;
Expand All @@ -42,9 +45,10 @@ describe("createWrappedAPIs()", () => {
dialog.deny(true);
},
});
suppliedPermissionStore = supplied.permissionStore;
suppliedUser = supplied.user;
suppliedUser.jumpToCoordinates(coordsA);
suppliedUser.grantPermission({ name: "geolocation" });
suppliedUser.grantAccess({ name: "geolocation" });

const wrapped = createWrappedAPIs({
geolocation: supplied.geolocation,
Expand All @@ -55,12 +59,13 @@ describe("createWrappedAPIs()", () => {
});
geolocation = wrapped.geolocation;
observer = wrapped.observer;
permissionStore = wrapped.permissionStore;
permissions = wrapped.permissions;
user = wrapped.user;
selectAPIs = wrapped.selectAPIs;
isUsingSuppliedAPIs = wrapped.isUsingSuppliedAPIs;
user.jumpToCoordinates(coordsB);
user.grantPermission({ name: "geolocation" });
user.grantAccess({ name: "geolocation" });

successCallback = vi.fn();
errorCallback = vi.fn();
Expand All @@ -86,7 +91,7 @@ describe("createWrappedAPIs()", () => {
});

it("delegates to the fake Permissions API", async () => {
expect(await user.requestAccess({ name: "push" })).toBe(true);
expect(await permissionStore.requestAccess({ name: "push" })).toBe(true);
expect((await permissions.query({ name: "push" })).state).toBe("granted");
});
});
Expand All @@ -111,7 +116,9 @@ describe("createWrappedAPIs()", () => {
});

it("delegates to the supplied Permissions API", async () => {
expect(await suppliedUser.requestAccess({ name: "push" })).toBe(false);
expect(
await suppliedPermissionStore.requestAccess({ name: "push" }),
).toBe(false);
expect((await permissions.query({ name: "push" })).state).toBe("denied");
});

Expand All @@ -126,7 +133,7 @@ describe("createWrappedAPIs()", () => {
it("observes the supplied Permissions API", async () => {
await expect(
observer.waitForPermissionState("denied", async () => {
suppliedUser.denyPermission({ name: "geolocation" });
suppliedUser.blockAccess({ name: "geolocation" });
}),
).resolves.toBeUndefined();
});
Expand All @@ -147,7 +154,9 @@ describe("createWrappedAPIs()", () => {
});

it("delegates to the fake Permissions API", async () => {
expect(await user.requestAccess({ name: "push" })).toBe(true);
expect(await permissionStore.requestAccess({ name: "push" })).toBe(
true,
);
expect((await permissions.query({ name: "push" })).state).toBe(
"granted",
);
Expand Down Expand Up @@ -175,7 +184,7 @@ describe("createWrappedAPIs()", () => {
});

it("delegates to the fake Permissions API", async () => {
expect(await user.requestAccess({ name: "push" })).toBe(true);
expect(await permissionStore.requestAccess({ name: "push" })).toBe(true);
expect((await permissions.query({ name: "push" })).state).toBe("granted");
});

Expand All @@ -190,7 +199,7 @@ describe("createWrappedAPIs()", () => {
it("observes the fake Permissions API", async () => {
await expect(
observer.waitForPermissionState("denied", async () => {
user.denyPermission({ name: "geolocation" });
user.blockAccess({ name: "geolocation" });
}),
).resolves.toBeUndefined();
});
Expand All @@ -211,7 +220,9 @@ describe("createWrappedAPIs()", () => {
});

it("delegates to the supplied Permissions API", async () => {
expect(await suppliedUser.requestAccess({ name: "push" })).toBe(false);
expect(
await suppliedPermissionStore.requestAccess({ name: "push" }),
).toBe(false);
expect((await permissions.query({ name: "push" })).state).toBe(
"denied",
);
Expand Down
Loading

0 comments on commit 9d21bf8

Please sign in to comment.