| 'use strict'; |
| |
| const { skipIfSQLiteMissing, spawnPromisified } = require('../common'); |
| skipIfSQLiteMissing(); |
| const tmpdir = require('../common/tmpdir'); |
| const assert = require('node:assert'); |
| const { join } = require('node:path'); |
| const { readdir } = require('node:fs/promises'); |
| const { test, describe } = require('node:test'); |
| let cnt = 0; |
| |
| tmpdir.refresh(); |
| |
| function nextLocalStorage() { |
| return join(tmpdir.path, `${++cnt}.localstorage`); |
| } |
| |
| test('Storage instances cannot be created in userland', async () => { |
| const cp = await spawnPromisified(process.execPath, [ |
| '-e', 'new globalThis.Storage()', |
| ]); |
| |
| assert.strictEqual(cp.code, 1); |
| assert.strictEqual(cp.signal, null); |
| assert.strictEqual(cp.stdout, ''); |
| assert.match(cp.stderr, /Error: Illegal constructor/); |
| }); |
| |
| test('calling "length" getter on invalid this throws', async () => { |
| assert.throws(() => Storage.prototype.length, TypeError); |
| const { get } = Object.getOwnPropertyDescriptor(Storage.prototype, 'length'); |
| for (const thisArg of [null, undefined, 1n, -0, NaN, true, false, '', [], {}, Symbol()]) { |
| assert.throws(() => get.call(thisArg), TypeError); |
| } |
| }); |
| |
| test('sessionStorage is not persisted', async () => { |
| let cp = await spawnPromisified(process.execPath, [ |
| '-pe', 'sessionStorage.foo = "barbaz"', |
| ]); |
| assert.strictEqual(cp.code, 0); |
| assert.match(cp.stdout, /barbaz/); |
| |
| cp = await spawnPromisified(process.execPath, [ |
| '-pe', 'sessionStorage.foo', |
| ]); |
| assert.strictEqual(cp.code, 0); |
| assert.match(cp.stdout, /undefined/); |
| assert.strictEqual((await readdir(tmpdir.path)).length, 0); |
| }); |
| |
| test('localStorage returns undefined and warns without --localstorage-file', async () => { |
| const cp = await spawnPromisified(process.execPath, [ |
| '-pe', 'localStorage', |
| ]); |
| assert.strictEqual(cp.code, 0); |
| assert.strictEqual(cp.signal, null); |
| assert.match(cp.stdout, /undefined/); |
| assert.match(cp.stderr, /ExperimentalWarning:.*localStorage is not available/); |
| }); |
| |
| test('localStorage is not enumerable without --localstorage-file', async () => { |
| const cp = await spawnPromisified(process.execPath, [ |
| '-pe', 'Object.keys(globalThis).includes("localStorage")', |
| ]); |
| assert.strictEqual(cp.code, 0); |
| assert.match(cp.stdout, /false/); |
| }); |
| |
| test('localStorage is not persisted if it is unused', async () => { |
| const cp = await spawnPromisified(process.execPath, [ |
| '--localstorage-file', nextLocalStorage(), |
| '-pe', 'localStorage === globalThis.localStorage', |
| ]); |
| assert.strictEqual(cp.code, 0); |
| assert.match(cp.stdout, /true/); |
| assert.strictEqual((await readdir(tmpdir.path)).length, 0); |
| }); |
| |
| test('localStorage is persisted if it is used', async () => { |
| const localStorageFile = nextLocalStorage(); |
| let cp = await spawnPromisified(process.execPath, [ |
| '--localstorage-file', localStorageFile, |
| '-pe', 'localStorage.foo = "barbaz"', |
| ]); |
| assert.strictEqual(cp.code, 0); |
| assert.match(cp.stdout, /barbaz/); |
| const entries = await readdir(tmpdir.path); |
| assert.strictEqual(entries.length, 1); |
| assert.match(entries[0], /\d+\.localstorage/); |
| |
| cp = await spawnPromisified(process.execPath, [ |
| '--localstorage-file', localStorageFile, |
| '-pe', 'localStorage.foo', |
| ]); |
| assert.strictEqual(cp.code, 0); |
| assert.match(cp.stdout, /barbaz/); |
| }); |
| |
| |
| describe('webstorage quota for localStorage and sessionStorage', () => { |
| const MAX_STORAGE_SIZE = 10 * 1024 * 1024; |
| |
| for (const storage of ['localStorage', 'sessionStorage']) { |
| test(`${storage} can store a max of 10 MB quota`, async () => { |
| const args = []; |
| if (storage === 'localStorage') { |
| args.push('--localstorage-file', nextLocalStorage()); |
| } |
| // Each character is 2 bytes |
| args.push('-e', ` |
| const assert = require('assert'); |
| ${storage}['a'.repeat(${MAX_STORAGE_SIZE} / 2)] = ''; |
| assert.throws( |
| () => { ${storage}.anything = 'should fail'; }, |
| (err) => { |
| assert.strictEqual(err.name, 'QuotaExceededError'); |
| assert.strictEqual(err.code, 22); |
| assert(err instanceof DOMException); |
| assert(err instanceof QuotaExceededError); |
| assert.strictEqual(err.quota, null); |
| assert.strictEqual(err.requested, null); |
| return true; |
| }, |
| ); |
| `); |
| const cp = await spawnPromisified(process.execPath, args); |
| assert.strictEqual(cp.code, 0); |
| }); |
| } |
| }); |
| |
| test('disabled with --no-webstorage', async () => { |
| for (const api of ['Storage', 'localStorage', 'sessionStorage']) { |
| const cp = await spawnPromisified(process.execPath, [ |
| '--no-webstorage', |
| '--localstorage-file', |
| './test/fixtures/localstoragefile-global-test', |
| '-e', |
| api, |
| ]); |
| |
| assert.strictEqual(cp.code, 1); |
| assert.strictEqual(cp.signal, null); |
| assert.strictEqual(cp.stdout, ''); |
| assert(cp.stderr.includes(`ReferenceError: ${api} is not defined`)); |
| } |
| }); |