mirror of
https://github.com/bitwarden/browser
synced 2026-02-21 20:04:02 +00:00
unit test achievement manager
This commit is contained in:
152
libs/common/src/tools/achievements/achievement-manager.spec.ts
Normal file
152
libs/common/src/tools/achievements/achievement-manager.spec.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import { BehaviorSubject, ReplaySubject, Subject, firstValueFrom } from "rxjs";
|
||||
|
||||
import { active } from "./achievement-manager";
|
||||
import {
|
||||
ItemCreatedTracker,
|
||||
TotallyAttachedValidator,
|
||||
UnboundItemCreatedTracker,
|
||||
} from "./examples/example-validators";
|
||||
import { AchievementId, AchievementValidator, MetricId } from "./types";
|
||||
|
||||
describe("active", () => {
|
||||
it("passes through empty achievement sets", async () => {
|
||||
const metrics$ = new BehaviorSubject<Map<MetricId, number>>(new Map());
|
||||
const earned$ = new BehaviorSubject<Set<AchievementId>>(new Set());
|
||||
const validators$ = new Subject<AchievementValidator[]>();
|
||||
const results$ = new ReplaySubject<AchievementValidator[]>(1);
|
||||
|
||||
validators$.pipe(active(metrics$, earned$)).subscribe(results$);
|
||||
const results = firstValueFrom(results$);
|
||||
validators$.next([]);
|
||||
|
||||
await expect(results).resolves.toEqual([]);
|
||||
});
|
||||
|
||||
it("passes through until-earned validators when earned$ omits the achievement id", async () => {
|
||||
const metrics$ = new BehaviorSubject<Map<MetricId, number>>(new Map());
|
||||
const earned$ = new BehaviorSubject<Set<AchievementId>>(new Set());
|
||||
const validators$ = new Subject<AchievementValidator[]>();
|
||||
const results$ = new ReplaySubject<AchievementValidator[]>(1);
|
||||
|
||||
validators$.pipe(active(metrics$, earned$)).subscribe(results$);
|
||||
const results = firstValueFrom(results$);
|
||||
validators$.next([TotallyAttachedValidator]);
|
||||
|
||||
await expect(results).resolves.toEqual([TotallyAttachedValidator]);
|
||||
});
|
||||
|
||||
it("filters until-earned validators when earned$ includes the achievement id", async () => {
|
||||
const metrics$ = new BehaviorSubject<Map<MetricId, number>>(new Map());
|
||||
const earned$ = new BehaviorSubject<Set<AchievementId>>(
|
||||
new Set([TotallyAttachedValidator.achievement]),
|
||||
);
|
||||
const validators$ = new Subject<AchievementValidator[]>();
|
||||
const results$ = new ReplaySubject<AchievementValidator[]>(1);
|
||||
|
||||
validators$.pipe(active(metrics$, earned$)).subscribe(results$);
|
||||
const results = firstValueFrom(results$);
|
||||
validators$.next([TotallyAttachedValidator]);
|
||||
|
||||
await expect(results).resolves.toEqual([]);
|
||||
});
|
||||
|
||||
it("passes through threshold validators when metric$ omits its metric and the low threshold isn't defined", async () => {
|
||||
const metrics$ = new BehaviorSubject<Map<MetricId, number>>(new Map());
|
||||
const earned$ = new BehaviorSubject<Set<AchievementId>>(
|
||||
new Set([ItemCreatedTracker.achievement]),
|
||||
);
|
||||
const validators$ = new Subject<AchievementValidator[]>();
|
||||
const results$ = new ReplaySubject<AchievementValidator[]>(1);
|
||||
|
||||
validators$.pipe(active(metrics$, earned$)).subscribe(results$);
|
||||
const results = firstValueFrom(results$);
|
||||
validators$.next([ItemCreatedTracker]);
|
||||
|
||||
await expect(results).resolves.toEqual([ItemCreatedTracker]);
|
||||
});
|
||||
|
||||
it("passes through threshold validators when metric$ includes a metric below the validator's `high` threshold", async () => {
|
||||
const metrics$ = new BehaviorSubject<Map<MetricId, number>>(
|
||||
new Map([[ItemCreatedTracker.active.metric, 0]]),
|
||||
);
|
||||
const earned$ = new BehaviorSubject<Set<AchievementId>>(
|
||||
new Set([ItemCreatedTracker.achievement]),
|
||||
);
|
||||
const validators$ = new Subject<AchievementValidator[]>();
|
||||
const results$ = new ReplaySubject<AchievementValidator[]>(1);
|
||||
|
||||
validators$.pipe(active(metrics$, earned$)).subscribe(results$);
|
||||
const results = firstValueFrom(results$);
|
||||
validators$.next([ItemCreatedTracker]);
|
||||
|
||||
await expect(results).resolves.toEqual([ItemCreatedTracker]);
|
||||
});
|
||||
|
||||
it("filters threshold validators when metric$ includes a metric equal to the validator's `high` threshold", async () => {
|
||||
const metrics$ = new BehaviorSubject<Map<MetricId, number>>(
|
||||
new Map([[ItemCreatedTracker.active.metric, 1]]),
|
||||
);
|
||||
const earned$ = new BehaviorSubject<Set<AchievementId>>(
|
||||
new Set([ItemCreatedTracker.achievement]),
|
||||
);
|
||||
const validators$ = new Subject<AchievementValidator[]>();
|
||||
const results$ = new ReplaySubject<AchievementValidator[]>(1);
|
||||
|
||||
validators$.pipe(active(metrics$, earned$)).subscribe(results$);
|
||||
const results = firstValueFrom(results$);
|
||||
validators$.next([ItemCreatedTracker]);
|
||||
|
||||
await expect(results).resolves.toEqual([]);
|
||||
});
|
||||
|
||||
it("filters threshold validators when metric$ includes a metric greater than the validator's `high` threshold", async () => {
|
||||
const metrics$ = new BehaviorSubject<Map<MetricId, number>>(
|
||||
new Map([[ItemCreatedTracker.active.metric, 2]]),
|
||||
);
|
||||
const earned$ = new BehaviorSubject<Set<AchievementId>>(
|
||||
new Set([ItemCreatedTracker.achievement]),
|
||||
);
|
||||
const validators$ = new Subject<AchievementValidator[]>();
|
||||
const results$ = new ReplaySubject<AchievementValidator[]>(1);
|
||||
|
||||
validators$.pipe(active(metrics$, earned$)).subscribe(results$);
|
||||
const results = firstValueFrom(results$);
|
||||
validators$.next([ItemCreatedTracker]);
|
||||
|
||||
await expect(results).resolves.toEqual([]);
|
||||
});
|
||||
|
||||
it("passes through threshold validators when metric$ includes a metric equal to the validator's `low` threshold", async () => {
|
||||
const metrics$ = new BehaviorSubject<Map<MetricId, number>>(
|
||||
new Map([[UnboundItemCreatedTracker.active.metric, 2]]),
|
||||
);
|
||||
const earned$ = new BehaviorSubject<Set<AchievementId>>(
|
||||
new Set([UnboundItemCreatedTracker.achievement]),
|
||||
);
|
||||
const validators$ = new Subject<AchievementValidator[]>();
|
||||
const results$ = new ReplaySubject<AchievementValidator[]>(1);
|
||||
|
||||
validators$.pipe(active(metrics$, earned$)).subscribe(results$);
|
||||
const results = firstValueFrom(results$);
|
||||
validators$.next([UnboundItemCreatedTracker]);
|
||||
|
||||
await expect(results).resolves.toEqual([UnboundItemCreatedTracker]);
|
||||
});
|
||||
|
||||
it("filters threshold validators when metric$ includes a metric below to the validator's `low` threshold", async () => {
|
||||
const metrics$ = new BehaviorSubject<Map<MetricId, number>>(
|
||||
new Map([[UnboundItemCreatedTracker.active.metric, 0]]),
|
||||
);
|
||||
const earned$ = new BehaviorSubject<Set<AchievementId>>(
|
||||
new Set([UnboundItemCreatedTracker.achievement]),
|
||||
);
|
||||
const validators$ = new Subject<AchievementValidator[]>();
|
||||
const results$ = new ReplaySubject<AchievementValidator[]>(1);
|
||||
|
||||
validators$.pipe(active(metrics$, earned$)).subscribe(results$);
|
||||
const results = firstValueFrom(results$);
|
||||
validators$.next([UnboundItemCreatedTracker]);
|
||||
|
||||
await expect(results).resolves.toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -25,7 +25,7 @@ function active(
|
||||
|
||||
// monitor disabled if outside of threshold
|
||||
const progress = (m.active.metric && metrics.get(m.active.metric)) || 0;
|
||||
if (progress > (m.active.high ?? Number.POSITIVE_INFINITY)) {
|
||||
if (progress >= (m.active.high ?? Number.POSITIVE_INFINITY)) {
|
||||
return false;
|
||||
} else if (progress < (m.active.low ?? 0)) {
|
||||
return false;
|
||||
|
||||
@@ -7,6 +7,7 @@ const CredentialGeneratedProgress = "credential-generated" as MetricId;
|
||||
|
||||
const TotallyAttachedAchievement = "totally-attached" as AchievementId;
|
||||
const ItemCreatedMetric = "item-created-metric" as AchievementId;
|
||||
const UnboundItemCreatedMetric = "unbound-item-created-metric" as AchievementId;
|
||||
const ItemCreatedAchievement = "item-created" as AchievementId;
|
||||
const ThreeItemsCreatedAchievement = "three-vault-items-created" as AchievementId;
|
||||
const FiveItemsCreatedAchievement = "five-vault-items-created" as AchievementId;
|
||||
@@ -49,6 +50,22 @@ const ItemCreatedTracker = {
|
||||
},
|
||||
} satisfies AchievementValidator;
|
||||
|
||||
const UnboundItemCreatedTracker = {
|
||||
achievement: UnboundItemCreatedMetric,
|
||||
name: `[TRACKER] ${ItemCreatedProgress}`,
|
||||
description: `Measures ${ItemCreatedProgress}`,
|
||||
validator: Type.Threshold,
|
||||
active: { metric: ItemCreatedProgress, low: 1 },
|
||||
hidden: true,
|
||||
trigger(item) {
|
||||
return item.action === "vault-item-added";
|
||||
},
|
||||
measure(item, progress) {
|
||||
const value = 1 + (progress.get(ItemCreatedProgress) ?? 0);
|
||||
return [progressEvent(ItemCreatedProgress, value)];
|
||||
},
|
||||
} satisfies AchievementValidator;
|
||||
|
||||
const ItemCreatedValidator = {
|
||||
achievement: ItemCreatedAchievement,
|
||||
name: "What an item!",
|
||||
@@ -92,7 +109,7 @@ const FiveItemsCreatedValidator = {
|
||||
name: "fiiivvve GoOoOoOolllllllD RIIIIIINGS!!!!!!",
|
||||
description: "Add five items to your vault",
|
||||
validator: Type.Threshold,
|
||||
active: { metric: ItemCreatedProgress, low: 4, high: 5 },
|
||||
active: { metric: ItemCreatedProgress, low: 3, high: 5 },
|
||||
hidden: false,
|
||||
trigger(item) {
|
||||
return item.action === "vault-item-added";
|
||||
@@ -112,6 +129,8 @@ export {
|
||||
TotallyAttachedValidator,
|
||||
ItemCreatedMetric,
|
||||
ItemCreatedTracker,
|
||||
UnboundItemCreatedMetric,
|
||||
UnboundItemCreatedTracker,
|
||||
ItemCreatedProgress,
|
||||
ItemCreatedAchievement,
|
||||
ItemCreatedValidator,
|
||||
|
||||
Reference in New Issue
Block a user