mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-17 22:31:26 +02:00
A browser-based roguelike synchronized with crypto block timing. A run is a race against the next block: reach the exit before the block lands, or the house wins. The same codebase runs the solo dungeon, the social Tavern, and operator-selected PvP and race rulesets, with separate free and paid leaderboards. Self-hostable under the MIT License. See README.md to run it, docs/ARCHITECTURE.md for the design, and docs/DEPLOY.md to operate an instance.
101 lines
3.5 KiB
JavaScript
101 lines
3.5 KiB
JavaScript
const STORAGE_KEY = 'wowngeon_paid_ack';
|
|
|
|
function policy(version, isTestNetwork = false) {
|
|
return {
|
|
policyVersion: version,
|
|
paidAcknowledgementRequired: true,
|
|
minimumAge: 18,
|
|
service: {
|
|
isTestNetwork,
|
|
currencyLabel: isTestNetwork ? 'sXMR' : 'WOW',
|
|
cryptoType: isTestNetwork ? 'XMR' : 'WOW'
|
|
},
|
|
links: { terms: '/terms', privacy: '/privacy', responsiblePlay: '/responsible-play' }
|
|
};
|
|
}
|
|
|
|
function response(value) {
|
|
return Promise.resolve({ ok: true, json: () => Promise.resolve(value) });
|
|
}
|
|
|
|
describe('browser commerce acknowledgement cache', () => {
|
|
let values;
|
|
|
|
beforeEach(() => {
|
|
jest.resetModules();
|
|
values = new Map();
|
|
global.sessionStorage = {
|
|
getItem: jest.fn(key => values.has(key) ? values.get(key) : null),
|
|
setItem: jest.fn((key, value) => values.set(key, value)),
|
|
removeItem: jest.fn(key => values.delete(key))
|
|
};
|
|
global.fetch = jest.fn();
|
|
delete global.CommerceConsent;
|
|
});
|
|
|
|
afterEach(() => {
|
|
delete global.fetch;
|
|
delete global.sessionStorage;
|
|
delete global.CommerceConsent;
|
|
});
|
|
|
|
test('accepts only the five-field canonical cache and clears extra data', async () => {
|
|
global.fetch.mockReturnValueOnce(response(policy('browser-v1')));
|
|
const consent = require('../html/js/legal/commerceConsent');
|
|
await consent.loadPolicy();
|
|
values.set(STORAGE_KEY, JSON.stringify({
|
|
policyVersion: 'browser-v1',
|
|
ageEligible: true,
|
|
termsRead: true,
|
|
riskAccepted: true,
|
|
testnetUnderstood: false,
|
|
acknowledgedAt: 'must-not-be-stored'
|
|
}));
|
|
|
|
expect(consent.acknowledgement()).toBeNull();
|
|
expect(values.has(STORAGE_KEY)).toBe(false);
|
|
});
|
|
|
|
test('server version rejection clears acceptance and refetches current policy', async () => {
|
|
global.fetch
|
|
.mockReturnValueOnce(response(policy('browser-v1')))
|
|
.mockReturnValueOnce(response(policy('browser-v2')));
|
|
const consent = require('../html/js/legal/commerceConsent');
|
|
await consent.loadPolicy();
|
|
values.set(STORAGE_KEY, JSON.stringify({
|
|
policyVersion: 'browser-v1',
|
|
ageEligible: true,
|
|
termsRead: true,
|
|
riskAccepted: true,
|
|
testnetUnderstood: false
|
|
}));
|
|
expect(consent.acknowledgement()).toEqual(expect.objectContaining({ policyVersion: 'browser-v1' }));
|
|
|
|
const refreshed = await consent.reject({ code: 'PAID_ACK_VERSION', policyVersion: 'browser-v2' });
|
|
|
|
expect(refreshed.policyVersion).toBe('browser-v2');
|
|
expect(global.fetch).toHaveBeenCalledTimes(2);
|
|
expect(consent.acknowledgement()).toBeNull();
|
|
expect(values.has(STORAGE_KEY)).toBe(false);
|
|
});
|
|
|
|
test('disconnect-facing clear removes both memory and session copies', async () => {
|
|
global.fetch.mockReturnValueOnce(response(policy('browser-v1')));
|
|
const consent = require('../html/js/legal/commerceConsent');
|
|
await consent.loadPolicy();
|
|
values.set(STORAGE_KEY, JSON.stringify({
|
|
policyVersion: 'browser-v1',
|
|
ageEligible: true,
|
|
termsRead: true,
|
|
riskAccepted: true,
|
|
testnetUnderstood: false
|
|
}));
|
|
expect(consent.acknowledgement()).not.toBeNull();
|
|
|
|
consent.clear();
|
|
|
|
expect(consent.acknowledgement()).toBeNull();
|
|
expect(values.has(STORAGE_KEY)).toBe(false);
|
|
});
|
|
});
|