chore: 整理 third-party Dark Reader 目录并移除本地说明文件跟踪
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
import type {HSLA} from '../../../src/utils/color';
|
||||
import {lowerCalcExpression, parse, hslToRGB, rgbToHSL, rgbToString, rgbToHexString, hslToString, getRGBValues} from '../../../src/utils/color';
|
||||
|
||||
test('Color parsing', () => {
|
||||
expect(parse('rgb(255,0,153)')).toEqual({r: 255, g: 0, b: 153, a: 1});
|
||||
expect(parse('rgb(255, 0, 153)')).toEqual({r: 255, g: 0, b: 153, a: 1});
|
||||
expect(parse('rgb(100%,0%,60%)')).toEqual({r: 255, g: 0, b: 153, a: 1});
|
||||
expect(parse('rgb(100%, 0%, 60%)')).toEqual({r: 255, g: 0, b: 153, a: 1});
|
||||
expect(parse('rgb(255 0 153)')).toEqual({r: 255, g: 0, b: 153, a: 1});
|
||||
|
||||
expect(parse('rgb(255, 0, 153, 1)')).toEqual({r: 255, g: 0, b: 153, a: 1});
|
||||
expect(parse('rgb(255, 0, 153, 100%)')).toEqual({r: 255, g: 0, b: 153, a: 1});
|
||||
expect(parse('rgb(255 0 153 / 1)')).toEqual({r: 255, g: 0, b: 153, a: 1});
|
||||
expect(parse('rgb(255 0 153 / 100%)')).toEqual({r: 255, g: 0, b: 153, a: 1});
|
||||
expect(parse('rgb(255, 0, 153.6, 1)')).toEqual({r: 255, g: 0, b: 154, a: 1});
|
||||
expect(parse('rgb(0 0 0/0.04)')).toEqual({r: 0, g: 0, b: 0, a: 0.04});
|
||||
expect(parse('rgb(1e2, .5e1, .5e0, +.25e2%)')).toEqual({r: 100, g: 5, b: 1, a: 0.25});
|
||||
|
||||
expect(parse('rgba(51, 170, 51, .1)')).toEqual({r: 51, g: 170, b: 51, a: 0.1});
|
||||
expect(parse('rgba(51, 170, 51, .4)')).toEqual({r: 51, g: 170, b: 51, a: 0.4});
|
||||
expect(parse('rgba(51, 170, 51, .7)')).toEqual({r: 51, g: 170, b: 51, a: 0.7});
|
||||
expect(parse('rgba(51, 170, 51, 1)')).toEqual({r: 51, g: 170, b: 51, a: 1});
|
||||
expect(parse('rgba(51 170 51 / 0.4)')).toEqual({r: 51, g: 170, b: 51, a: 0.4});
|
||||
expect(parse('rgba(51 170 51 / 40%)')).toEqual({r: 51, g: 170, b: 51, a: 0.4});
|
||||
expect(parse('rgba(255, 0, 153.6, 1)')).toEqual({r: 255, g: 0, b: 154, a: 1});
|
||||
expect(parse('rgba(1e2, .5e1, .5e0, +.25e2%)')).toEqual({r: 100, g: 5, b: 1, a: 0.25});
|
||||
|
||||
expect(parse('hsl(270,60%,70%)')).toEqual({r: 179, g: 133, b: 224, a: 1});
|
||||
expect(parse('hsl(270, 60%, 70%)')).toEqual({r: 179, g: 133, b: 224, a: 1});
|
||||
expect(parse('hsl(270 60% 70%)')).toEqual({r: 179, g: 133, b: 224, a: 1});
|
||||
expect(parse('hsl(270deg, 60%, 70%)')).toEqual({r: 179, g: 133, b: 224, a: 1});
|
||||
expect(parse('hsl(4.71239rad, 60%, 70%)')).toEqual({r: 179, g: 133, b: 224, a: 1});
|
||||
expect(parse('hsl(.75turn, 60%, 70%)')).toEqual({r: 179, g: 133, b: 224, a: 1});
|
||||
|
||||
expect(parse('hsl(270, 60%, 50%, .15)')).toEqual({r: 128, g: 51, b: 204, a: 0.15});
|
||||
expect(parse('hsl(270, 60%, 50%, 15%)')).toEqual({r: 128, g: 51, b: 204, a: 0.15});
|
||||
expect(parse('hsl(270 60% 50% / .15)')).toEqual({r: 128, g: 51, b: 204, a: 0.15});
|
||||
expect(parse('hsl(270 60% 50% / 15%)')).toEqual({r: 128, g: 51, b: 204, a: 0.15});
|
||||
|
||||
expect(parse('hsla(240, 100%, 50%, .05)')).toEqual({r: 0, g: 0, b: 255, a: 0.05});
|
||||
expect(parse('hsla(240, 100%, 50%, .4)')).toEqual({r: 0, g: 0, b: 255, a: 0.4});
|
||||
expect(parse('hsla(240, 100%, 50%, .7)')).toEqual({r: 0, g: 0, b: 255, a: 0.7});
|
||||
expect(parse('hsla(240, 100%, 50%, 1)')).toEqual({r: 0, g: 0, b: 255, a: 1});
|
||||
expect(parse('hsla(240 100% 50% / .05)')).toEqual({r: 0, g: 0, b: 255, a: 0.05});
|
||||
expect(parse('hsla(240 100% 50% / 5%)')).toEqual({r: 0, g: 0, b: 255, a: 0.05});
|
||||
|
||||
expect(parse('#f09')).toEqual({r: 255, g: 0, b: 153, a: 1});
|
||||
expect(parse('#F09')).toEqual({r: 255, g: 0, b: 153, a: 1});
|
||||
expect(parse('#ff0099')).toEqual({r: 255, g: 0, b: 153, a: 1});
|
||||
expect(parse('#FF0099')).toEqual({r: 255, g: 0, b: 153, a: 1});
|
||||
|
||||
expect(parse('#3a30')).toEqual({r: 51, g: 170, b: 51, a: 0});
|
||||
expect(parse('#3A3F')).toEqual({r: 51, g: 170, b: 51, a: 1});
|
||||
expect(parse('#33aa3300')).toEqual({r: 51, g: 170, b: 51, a: 0});
|
||||
expect(parse('#33AA3388')).toEqual({r: 51, g: 170, b: 51, a: 136 / 255});
|
||||
|
||||
expect(parse('rebeccapurple')).toEqual({r: 102, g: 51, b: 153, a: 1});
|
||||
|
||||
expect(parse('transparent')).toEqual({r: 0, g: 0, b: 0, a: 0});
|
||||
|
||||
expect(parse('InfoBackground')).toEqual({r: 251, g: 252, b: 197, a: 1});
|
||||
expect(parse('-webkit-focus-ring-color')).toEqual({r: 229, g: 151, b: 0, a: 1});
|
||||
|
||||
expect(parse('sponge')).toBeNull();
|
||||
expect(parse('hsl(0, 0%, 0%) rgb(0, 0, 0)')).toBeNull();
|
||||
expect(parse('#hello')).toBeNull();
|
||||
});
|
||||
|
||||
test('Stringify color', () => {
|
||||
expect(rgbToString({r: 255, g: 0, b: 153})).toEqual('rgb(255, 0, 153)');
|
||||
expect(rgbToString({r: 255, g: 0, b: 153, a: 1})).toEqual('rgb(255, 0, 153)');
|
||||
expect(rgbToString({r: 255, g: 0, b: 153, a: 0.25003})).toEqual('rgba(255, 0, 153, 0.25)');
|
||||
|
||||
expect(rgbToHexString({r: 255, g: 0, b: 153})).toEqual('#ff0099');
|
||||
expect(rgbToHexString({r: 255, g: 0, b: 153, a: 1})).toEqual('#ff0099');
|
||||
expect(rgbToHexString({r: 255, g: 0, b: 153, a: 0.25003})).toEqual('#ff009940');
|
||||
|
||||
expect(hslToString({h: 270, s: 0.6, l: 0.7})).toEqual('hsl(270, 60%, 70%)');
|
||||
expect(hslToString({h: 270, s: 0.6, l: 0.7, a: 1})).toEqual('hsl(270, 60%, 70%)');
|
||||
expect(hslToString({h: 270.25, s: 0.5988, l: 0.702, a: 0.33333})).toEqual('hsla(270, 60%, 70%, 0.33)');
|
||||
expect(hslToString({h: 270.25, s: 0.5988, l: 0.702, a: 0.00032})).toEqual('hsla(270, 60%, 70%, 0)');
|
||||
});
|
||||
|
||||
test('Color conversion', () => {
|
||||
expect(hslToRGB({h: 180, s: 1, l: 0.5, a: 0.25})).toEqual({r: 0, g: 255, b: 255, a: 0.25});
|
||||
expect(hslToRGB({h: 0, s: 1, l: 0.25, a: 0.5})).toEqual({r: 128, g: 0, b: 0, a: 0.5});
|
||||
expect(hslToRGB({h: 12, s: 0.78, l: 0.61})).toEqual({r: 233, g: 109, b: 78, a: 1});
|
||||
expect(hslToRGB({h: 192, s: 0.57, l: 0.10})).toEqual({r: 11, g: 34, b: 40, a: 1});
|
||||
expect(hslToRGB({h: 0, s: 0, l: 0.5})).toEqual({r: 128, g: 128, b: 128, a: 1});
|
||||
|
||||
const round = (color: HSLA) => Object.entries(color).reduce((c, [k, v]) => (c[k as keyof HSLA] = k === 'h' ? Math.round(v) : Math.round(v * 100) / 100, c), {} as HSLA);
|
||||
expect(round(rgbToHSL({r: 0, g: 255, b: 255, a: 0.25}))).toEqual({h: 180, s: 1, l: 0.5, a: 0.25});
|
||||
expect(round(rgbToHSL({r: 128, g: 0, b: 0, a: 0.5}))).toEqual({h: 0, s: 1, l: 0.25, a: 0.5});
|
||||
expect(round(rgbToHSL({r: 233, g: 109, b: 78}))).toEqual({h: 12, s: 0.78, l: 0.61, a: 1});
|
||||
expect(round(rgbToHSL({r: 11, g: 34, b: 40}))).toEqual({h: 192, s: 0.57, l: 0.10, a: 1});
|
||||
expect(round(rgbToHSL({r: 161, g: 28, b: 61}))).toEqual({h: 345, s: 0.7, l: 0.37, a: 1});
|
||||
});
|
||||
|
||||
test('Lower calc expressions', () => {
|
||||
expect(lowerCalcExpression('hsl(0, 0%, calc(95% - 3%))')).toEqual('hsl(0, 0%, 92%)');
|
||||
expect(lowerCalcExpression('hsl(0, calc(25% + 12%), calc(95% - 3%))')).toEqual('hsl(0, 37%, 92%)');
|
||||
expect(lowerCalcExpression('rgb(calc(216.75 + 153 * .15), calc(216.75 + 205 * .15), calc(216.75 + 255 * .15))')).toEqual('rgb(240, 248, 255)');
|
||||
});
|
||||
|
||||
test('RGB numbers', () => {
|
||||
expect(getRGBValues('0 0 0')).toEqual([0, 0, 0, 1]);
|
||||
expect(getRGBValues('255 255 255')).toEqual([255, 255, 255, 1]);
|
||||
|
||||
expect(getRGBValues('0, 0, 0')).toEqual([0, 0, 0, 1]);
|
||||
expect(getRGBValues('255, 255, 255')).toEqual([255, 255, 255, 1]);
|
||||
|
||||
expect(getRGBValues('0 0 0 / 0.5')).toEqual([0, 0, 0, 0.5]);
|
||||
expect(getRGBValues('255 255 255 / 0.25')).toEqual([255, 255, 255, 0.25]);
|
||||
|
||||
expect(getRGBValues('0, 0, 0, 0.5')).toEqual([0, 0, 0, 0.5]);
|
||||
expect(getRGBValues('255, 255, 255, 0.25')).toEqual([255, 255, 255, 0.25]);
|
||||
|
||||
expect(getRGBValues('0, 0, 0, .5')).toEqual([0, 0, 0, 0.5]);
|
||||
expect(getRGBValues('255, 255, 255, .25')).toEqual([255, 255, 255, 0.25]);
|
||||
|
||||
expect(getRGBValues('0 0 0 / 50%')).toEqual([0, 0, 0, 0.5]);
|
||||
expect(getRGBValues('255 255 255 / 50%')).toEqual([255, 255, 255, 0.5]);
|
||||
|
||||
expect(getRGBValues('50% 0 25% / 50%')).toEqual([128, 0, 64, 0.5]);
|
||||
expect(getRGBValues('.8% 0 255 / 12.5%')).toEqual([2, 0, 255, 0.125]);
|
||||
|
||||
expect(getRGBValues('255 , 255 , 255')).toEqual([255, 255, 255, 1]);
|
||||
expect(getRGBValues('50%0%25%/50%')).toEqual([128, 0, 64, 0.5]);
|
||||
|
||||
expect(getRGBValues('0')).toEqual(null);
|
||||
expect(getRGBValues('0 0')).toEqual(null);
|
||||
expect(getRGBValues('0, 0')).toEqual(null);
|
||||
expect(getRGBValues('0%, 0%, 0%, 0%')).toEqual(null);
|
||||
expect(getRGBValues('0%, 0%, 0% / 0%')).toEqual(null);
|
||||
expect(getRGBValues('%')).toEqual(null);
|
||||
expect(getRGBValues('.')).toEqual(null);
|
||||
expect(getRGBValues('. . .')).toEqual(null);
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
import {scale, clamp, multiplyMatrices} from '../../../src/utils/math';
|
||||
|
||||
test('Scale', () => {
|
||||
expect(scale(5, 0, 10, 0, 100)).toEqual(50);
|
||||
});
|
||||
|
||||
test('Clamp', () => {
|
||||
expect(clamp(1, 2, 3)).toEqual(2);
|
||||
expect(clamp(2, 1, 3)).toEqual(2);
|
||||
expect(clamp(3, 1, 2)).toEqual(2);
|
||||
});
|
||||
|
||||
test('Matrix multiplication', () => {
|
||||
expect(multiplyMatrices([
|
||||
[1, 2, 3, 4, 5],
|
||||
[6, 7, 8, 9, 10],
|
||||
[11, 12, 13, 14, 15],
|
||||
[16, 17, 18, 19, 20],
|
||||
[21, 22, 23, 24, 25],
|
||||
],
|
||||
[
|
||||
[1], [2], [3], [4], [5],
|
||||
])).toEqual([
|
||||
[55], [130], [205], [280], [355],
|
||||
]);
|
||||
|
||||
expect(multiplyMatrices([
|
||||
[1, 2, 3, 4, 5],
|
||||
[6, 7, 8, 9, 10],
|
||||
[11, 12, 13, 14, 15],
|
||||
[16, 17, 18, 19, 20],
|
||||
[21, 22, 23, 24, 25],
|
||||
],
|
||||
[
|
||||
[1, 2, 3, 4, 5],
|
||||
[6, 7, 8, 9, 10],
|
||||
[11, 12, 13, 14, 15],
|
||||
[16, 17, 18, 19, 20],
|
||||
[21, 22, 23, 24, 25],
|
||||
])).toEqual([
|
||||
[215, 230, 245, 260, 275],
|
||||
[490, 530, 570, 610, 650],
|
||||
[765, 830, 895, 960, 1025],
|
||||
[1040, 1130, 1220, 1310, 1400],
|
||||
[1315, 1430, 1545, 1660, 1775],
|
||||
]);
|
||||
// expect(multiplyMatrices([[1, 2, 3], [6, 5, 3]], [[1, 2], [7, 4], [6, 2]])).toEqual([[33, 16], [59, 38]]);
|
||||
});
|
||||
@@ -0,0 +1,119 @@
|
||||
import {parseCSS} from '../../../src/utils/css-text/parse-css';
|
||||
import {parseGradient} from '../../../src/utils/css-text/parse-gradient';
|
||||
|
||||
test('Parse CSS', () => {
|
||||
const cssText = '@media all { div { } img[src*="a,b;c"] { background-color: black; background-image: url(data:image/gif;base64,XYZ); color: red !important; } } @media print, screen and (min-width: 20rem) { h1, h2 { } } c-wiz[data-p^="%.@.[[1,3"] { color: green; }';
|
||||
const parsedCSS = parseCSS(cssText);
|
||||
expect(parsedCSS).toEqual([
|
||||
{
|
||||
type: '@media',
|
||||
query: 'all',
|
||||
rules: [
|
||||
{
|
||||
selectors: ['div'],
|
||||
declarations: [],
|
||||
},
|
||||
{
|
||||
selectors: ['img[src*="a,b;c"]'],
|
||||
declarations: [
|
||||
{
|
||||
property: 'background-color',
|
||||
value: 'black',
|
||||
important: false,
|
||||
},
|
||||
{
|
||||
property: 'background-image',
|
||||
value: 'url(data:image/gif;base64,XYZ)',
|
||||
important: false,
|
||||
},
|
||||
{
|
||||
property: 'color',
|
||||
value: 'red',
|
||||
important: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: '@media',
|
||||
query: 'print, screen and (min-width: 20rem)',
|
||||
rules: [
|
||||
{
|
||||
selectors: ['h1', 'h2'],
|
||||
declarations: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
selectors: ['c-wiz[data-p^="%.@.[[1,3"]'],
|
||||
declarations: [
|
||||
{
|
||||
property: 'color',
|
||||
value: 'green',
|
||||
important: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('type gradients', () => {
|
||||
expect(parseGradient('linear-gradient(rgb(200))')).toEqual([{
|
||||
typeGradient: 'linear-gradient',
|
||||
match: 'rgb(200)',
|
||||
offset: 17,
|
||||
index: 0,
|
||||
hasComma: false,
|
||||
}]);
|
||||
expect(parseGradient(`linear-gradient(217deg, rgba(255,0,0,.8), rgba(255,0,0,0) 70.71%),
|
||||
linear-gradient(127deg, rgba(0,255,0,.8), rgba(0,255,0,0) 70.71%),
|
||||
linear-gradient(336deg, rgba(0,0,255,.8), rgba(0,0,255,0) 70.71%);`)).toEqual([
|
||||
{
|
||||
offset: 17,
|
||||
match: '217deg, rgba(255,0,0,.8), rgba(255,0,0,0) 70.71%',
|
||||
hasComma: true,
|
||||
index: 0,
|
||||
typeGradient: 'linear-gradient',
|
||||
},
|
||||
{
|
||||
typeGradient: 'linear-gradient',
|
||||
match: '127deg, rgba(0,255,0,.8), rgba(0,255,0,0) 70.71%',
|
||||
hasComma: true,
|
||||
offset: 17,
|
||||
index: 71,
|
||||
},
|
||||
{
|
||||
typeGradient: 'linear-gradient',
|
||||
match: '336deg, rgba(0,0,255,.8), rgba(0,0,255,0) 70.71%',
|
||||
hasComma: false,
|
||||
offset: 17,
|
||||
index: 142,
|
||||
},
|
||||
]);
|
||||
expect(parseGradient('radial-gradient(rgb(200))')).toEqual([{
|
||||
typeGradient: 'radial-gradient',
|
||||
match: 'rgb(200)',
|
||||
index: 0,
|
||||
offset: 17,
|
||||
hasComma: false,
|
||||
|
||||
}]);
|
||||
|
||||
expect(parseGradient('repeating-linear-gradient(transparent, #4d9f0c 40px), repeating-linear-gradient(0.25turn, transparent, #3f87a6 20px);')).toEqual([
|
||||
{
|
||||
typeGradient: 'repeating-linear-gradient',
|
||||
match: 'transparent, #4d9f0c 40px',
|
||||
hasComma: true,
|
||||
offset: 27,
|
||||
index: 0,
|
||||
},
|
||||
{
|
||||
typeGradient: 'repeating-linear-gradient',
|
||||
match: '0.25turn, transparent, #3f87a6 20px',
|
||||
hasComma: false,
|
||||
index: 54,
|
||||
offset: 27,
|
||||
},
|
||||
]);
|
||||
});
|
||||
@@ -0,0 +1,99 @@
|
||||
import {expect, jest, test} from '@jest/globals';
|
||||
|
||||
import {PromiseBarrier} from '../../../src/utils/promise-barrier';
|
||||
|
||||
describe('Promise barrier utility', () => {
|
||||
test('basic functionality', async () => {
|
||||
const barrier = new PromiseBarrier();
|
||||
const fn1 = jest.fn();
|
||||
const fn2 = jest.fn();
|
||||
(async () => {
|
||||
fn1();
|
||||
const result = await barrier.entry();
|
||||
fn2(result);
|
||||
})();
|
||||
expect(fn1).toHaveBeenCalled();
|
||||
expect(fn2).not.toHaveBeenCalled();
|
||||
|
||||
expect(barrier.isPending()).toBe(true);
|
||||
expect(barrier.isFulfilled()).toBe(false);
|
||||
expect(barrier.isRejected()).toBe(false);
|
||||
await barrier.resolve(2);
|
||||
expect(barrier.isFulfilled()).toBe(true);
|
||||
expect(barrier.isPending()).toBe(false);
|
||||
expect(barrier.isRejected()).toBe(false);
|
||||
expect(fn1).toHaveBeenCalledTimes(1);
|
||||
expect(fn2).toHaveBeenCalledWith(2);
|
||||
});
|
||||
|
||||
test('awaiting for barrier after it was settled', async () => {
|
||||
const barrierFulfilled = new PromiseBarrier();
|
||||
expect(barrierFulfilled.isPending()).toBe(true);
|
||||
expect(barrierFulfilled.isFulfilled()).toBe(false);
|
||||
expect(barrierFulfilled.isRejected()).toBe(false);
|
||||
const promise1 = barrierFulfilled.resolve('Hello World!');
|
||||
expect(barrierFulfilled.isFulfilled()).toBe(true);
|
||||
expect(barrierFulfilled.isPending()).toBe(false);
|
||||
expect(barrierFulfilled.isRejected()).toBe(false);
|
||||
const fn1 = jest.fn();
|
||||
await (async () => fn1(await barrierFulfilled.entry()))();
|
||||
await promise1;
|
||||
expect(fn1).toHaveBeenCalledWith('Hello World!');
|
||||
|
||||
const barrierRejected = new PromiseBarrier();
|
||||
const promise2 = barrierRejected.reject('rejection reason');
|
||||
const fn2 = jest.fn();
|
||||
await (async () => {
|
||||
try {
|
||||
await barrierRejected.entry();
|
||||
} catch (e) {
|
||||
fn2(e);
|
||||
}
|
||||
})();
|
||||
await promise2;
|
||||
expect(fn2).toHaveBeenCalledWith('rejection reason');
|
||||
});
|
||||
|
||||
test('resolving multiple times', async () => {
|
||||
const barrierFulfilled = new PromiseBarrier();
|
||||
expect(barrierFulfilled.isPending()).toBe(true);
|
||||
expect(barrierFulfilled.isFulfilled()).toBe(false);
|
||||
expect(barrierFulfilled.isRejected()).toBe(false);
|
||||
await barrierFulfilled.resolve('Hello World!');
|
||||
expect(barrierFulfilled.isFulfilled()).toBe(true);
|
||||
expect(barrierFulfilled.isPending()).toBe(false);
|
||||
expect(barrierFulfilled.isRejected()).toBe(false);
|
||||
await barrierFulfilled.resolve('Hello World 2!');
|
||||
expect(barrierFulfilled.isFulfilled()).toBe(true);
|
||||
expect(barrierFulfilled.isPending()).toBe(false);
|
||||
expect(barrierFulfilled.isRejected()).toBe(false);
|
||||
|
||||
const fn1 = jest.fn();
|
||||
await (async () => fn1(await barrierFulfilled.entry()))();
|
||||
|
||||
expect(fn1).toHaveBeenCalledWith('Hello World!');
|
||||
});
|
||||
|
||||
test('rejecting multiple times', async () => {
|
||||
const barrierRejected = new PromiseBarrier();
|
||||
expect(barrierRejected.isPending()).toBe(true);
|
||||
expect(barrierRejected.isFulfilled()).toBe(false);
|
||||
expect(barrierRejected.isRejected()).toBe(false);
|
||||
await barrierRejected.reject('rejection reason');
|
||||
expect(barrierRejected.isRejected()).toBe(true);
|
||||
expect(barrierRejected.isPending()).toBe(false);
|
||||
expect(barrierRejected.isFulfilled()).toBe(false);
|
||||
await barrierRejected.reject('rejection reason 2');
|
||||
|
||||
const fn2 = jest.fn();
|
||||
await (async () => {
|
||||
try {
|
||||
await barrierRejected.entry();
|
||||
} catch (e) {
|
||||
fn2(e);
|
||||
}
|
||||
})();
|
||||
|
||||
expect(fn2).toHaveBeenCalledWith('rejection reason');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,644 @@
|
||||
import {expect, jest, test} from '@jest/globals';
|
||||
|
||||
import {StateManagerImpl} from '../../../src/utils/state-manager-impl';
|
||||
|
||||
class PromiseWrapper {
|
||||
promises: Map<Promise<void>, 'pending' | 'resolved' | 'rejected'>;
|
||||
|
||||
constructor() {
|
||||
this.promises = new Map();
|
||||
}
|
||||
|
||||
add(promises: Promise<void> | Array<Promise<void>>) {
|
||||
if (!Array.isArray(promises)) {
|
||||
promises = [promises];
|
||||
}
|
||||
promises.forEach((promise) => {
|
||||
if (this.promises.has(promise)) {
|
||||
throw new Error('Already added');
|
||||
}
|
||||
this.promises.set(promise, 'pending');
|
||||
promise.then(() => this.promises.set(promise, 'resolved'))
|
||||
.catch(() => this.promises.set(promise, 'rejected'));
|
||||
});
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.promises.clear();
|
||||
}
|
||||
|
||||
getState(promise: Promise<void>) {
|
||||
return this.promises.get(promise);
|
||||
}
|
||||
|
||||
all(state: 'pending' | 'resolved' | 'rejected') {
|
||||
this.promises.forEach((s) => expect(s).toEqual(state));
|
||||
}
|
||||
}
|
||||
|
||||
describe('State manager utility', () => {
|
||||
const nextTick = () => new Promise((r) => setTimeout(r));
|
||||
const noop = () => { /* noop */ };
|
||||
|
||||
test('State manager basic functionality', async () => {
|
||||
const key = 'key';
|
||||
const storage: any = {
|
||||
fromStorage: 'fromStorage',
|
||||
};
|
||||
|
||||
const getMock = jest.fn();
|
||||
const get = (storageKey: string, callback: any) => {
|
||||
expect(storageKey).toEqual(key);
|
||||
getMock();
|
||||
callback({});
|
||||
};
|
||||
|
||||
const setMock = jest.fn();
|
||||
const set = (items: any, callback: () => void) => {
|
||||
Object.assign(storage, items);
|
||||
setMock();
|
||||
callback();
|
||||
};
|
||||
|
||||
const parent: any = {
|
||||
noSync: true,
|
||||
fromParent: 'fromParent',
|
||||
fromStorage: undefined,
|
||||
};
|
||||
|
||||
const stateManager = new StateManagerImpl(key, parent, {
|
||||
fromParent: 'fromDefault',
|
||||
fromStorage: 'fromDefault',
|
||||
}, {get, set}, noop, noop);
|
||||
|
||||
expect(getMock).not.toHaveBeenCalled();
|
||||
expect(setMock).not.toHaveBeenCalled();
|
||||
expect(parent).toEqual({
|
||||
noSync: true,
|
||||
fromParent: 'fromParent',
|
||||
fromStorage: undefined,
|
||||
});
|
||||
|
||||
await stateManager.loadState();
|
||||
expect(getMock).toHaveBeenCalled();
|
||||
expect(setMock).not.toHaveBeenCalled();
|
||||
expect(parent).toEqual({
|
||||
noSync: true,
|
||||
fromParent: 'fromDefault',
|
||||
fromStorage: 'fromDefault',
|
||||
});
|
||||
|
||||
parent.other = 'another';
|
||||
await stateManager.saveState();
|
||||
expect(setMock).toHaveBeenCalled();
|
||||
expect(storage[key]).toEqual({
|
||||
fromParent: 'fromDefault',
|
||||
fromStorage: 'fromDefault',
|
||||
});
|
||||
expect(parent).toEqual({
|
||||
fromParent: 'fromDefault',
|
||||
fromStorage: 'fromDefault',
|
||||
noSync: true,
|
||||
other: 'another',
|
||||
});
|
||||
});
|
||||
|
||||
test('State manager handles multiple concurrent loadState() calls', async () => {
|
||||
const key = 'key';
|
||||
|
||||
let getCount = 0;
|
||||
let getCallback: (data: any) => void;
|
||||
const get = (storageKey: string, callback: (data: any) => void) => {
|
||||
getCount++;
|
||||
expect(storageKey).toEqual(key);
|
||||
getCallback = callback;
|
||||
};
|
||||
|
||||
const setMock = jest.fn();
|
||||
|
||||
const parent: any = {
|
||||
noSync: true,
|
||||
data: 'fromParent',
|
||||
};
|
||||
|
||||
const stateManager = new StateManagerImpl(key, parent, {
|
||||
data: 'fromStorage',
|
||||
}, {get, set: setMock}, noop, noop);
|
||||
|
||||
expect(setMock).not.toHaveBeenCalled();
|
||||
expect(parent).toEqual({
|
||||
noSync: true,
|
||||
data: 'fromParent',
|
||||
});
|
||||
|
||||
// Multiple calls to loadState() should result in a single call to c.s.l.get()
|
||||
const promises = new PromiseWrapper();
|
||||
for (let i = 0; i < 5; i++) {
|
||||
promises.add(stateManager.loadState());
|
||||
}
|
||||
expect(getCount).toEqual(1);
|
||||
expect(parent).toEqual({
|
||||
noSync: true,
|
||||
data: 'fromParent',
|
||||
});
|
||||
promises.all('pending');
|
||||
|
||||
expect(getCount).toEqual(1);
|
||||
|
||||
getCallback!({});
|
||||
|
||||
promises.all('pending');
|
||||
|
||||
await nextTick();
|
||||
promises.all('resolved');
|
||||
expect(getCount).toEqual(1);
|
||||
expect(setMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('State manager handles multiple concurrent saveState() calls', async () => {
|
||||
const key = 'key';
|
||||
const storage: any = {};
|
||||
|
||||
const get = (storageKey: string, callback: (data: any) => void) => {
|
||||
expect(storageKey).toEqual(key);
|
||||
callback({[storageKey]: storage[storageKey]});
|
||||
};
|
||||
|
||||
const resolveSet = () => setCallback();
|
||||
let setCount = 0;
|
||||
let setCallback: () => void;
|
||||
const set = (items: any, callback: () => void) => {
|
||||
setCount++;
|
||||
setCallback = () => {
|
||||
Object.assign(storage, items);
|
||||
callback();
|
||||
};
|
||||
};
|
||||
|
||||
const parent: any = {
|
||||
noSync: true,
|
||||
count: 100,
|
||||
};
|
||||
|
||||
const stateManager = new StateManagerImpl(key, parent, {
|
||||
count: 0,
|
||||
}, {get, set}, noop, noop);
|
||||
|
||||
expect(parent).toEqual({
|
||||
noSync: true,
|
||||
count: 100,
|
||||
});
|
||||
|
||||
await stateManager.loadState();
|
||||
|
||||
expect(parent).toEqual({
|
||||
noSync: true,
|
||||
count: 0,
|
||||
});
|
||||
|
||||
// Multiple calls to loadState() should result in a single call to c.s.l.get()
|
||||
const promises = new PromiseWrapper();
|
||||
for (let i = 0; i < 5; i++) {
|
||||
parent.count++;
|
||||
promises.add(stateManager.saveState());
|
||||
}
|
||||
expect(setCount).toEqual(1);
|
||||
expect(parent).toEqual({
|
||||
noSync: true,
|
||||
count: 5,
|
||||
});
|
||||
promises.all('pending');
|
||||
|
||||
|
||||
// Resolve the first write
|
||||
expect(setCount).toEqual(1);
|
||||
resolveSet();
|
||||
expect(setCount).toEqual(2);
|
||||
|
||||
promises.all('pending');
|
||||
|
||||
await nextTick();
|
||||
promises.all('pending');
|
||||
|
||||
expect(parent).toEqual({
|
||||
noSync: true,
|
||||
count: 5,
|
||||
});
|
||||
|
||||
// Resolve the second write
|
||||
resolveSet();
|
||||
|
||||
expect(setCount).toEqual(2);
|
||||
expect(storage).toEqual({
|
||||
key: {
|
||||
count: 5,
|
||||
},
|
||||
});
|
||||
|
||||
await nextTick();
|
||||
promises.all('resolved');
|
||||
});
|
||||
|
||||
test('State manager handles multiple concurrent saveState() calls', async () => {
|
||||
const key = 'key';
|
||||
const storage: any = {};
|
||||
|
||||
const get = (storageKey: string, callback: (data: any) => void) => {
|
||||
expect(storageKey).toEqual(key);
|
||||
callback({[storageKey]: storage[storageKey]});
|
||||
};
|
||||
|
||||
const resolveSet = () => setCallback();
|
||||
let setCount = 0;
|
||||
let setCallback: () => void;
|
||||
const set = (items: any, callback: () => void) => {
|
||||
setCount++;
|
||||
setCallback = () => {
|
||||
Object.assign(storage, items);
|
||||
callback();
|
||||
};
|
||||
};
|
||||
|
||||
const parent: any = {
|
||||
noSync: true,
|
||||
count: 100,
|
||||
};
|
||||
|
||||
const stateManager = new StateManagerImpl(key, parent, {
|
||||
count: 0,
|
||||
}, {get, set}, noop, noop);
|
||||
|
||||
expect(parent).toEqual({
|
||||
noSync: true,
|
||||
count: 100,
|
||||
});
|
||||
|
||||
await stateManager.loadState();
|
||||
|
||||
expect(parent).toEqual({
|
||||
noSync: true,
|
||||
count: 0,
|
||||
});
|
||||
|
||||
// Multiple calls to loadState() should result in a single call to c.s.l.get()
|
||||
const promises = new PromiseWrapper();
|
||||
for (let i = 0; i < 5; i++) {
|
||||
parent.count++;
|
||||
promises.add(stateManager.saveState());
|
||||
}
|
||||
expect(setCount).toEqual(1);
|
||||
expect(parent).toEqual({
|
||||
noSync: true,
|
||||
count: 5,
|
||||
});
|
||||
promises.all('pending');
|
||||
|
||||
|
||||
// Resolve the first write
|
||||
expect(setCount).toEqual(1);
|
||||
resolveSet();
|
||||
expect(setCount).toEqual(2);
|
||||
|
||||
promises.all('pending');
|
||||
|
||||
// Wait for the next tick when all loadState promises resolve
|
||||
await nextTick();
|
||||
promises.all('pending');
|
||||
|
||||
expect(parent).toEqual({
|
||||
noSync: true,
|
||||
count: 5,
|
||||
});
|
||||
|
||||
// Resolve the second write
|
||||
resolveSet();
|
||||
expect(setCount).toEqual(2);
|
||||
expect(storage).toEqual({
|
||||
key: {
|
||||
count: 5,
|
||||
},
|
||||
});
|
||||
|
||||
await nextTick();
|
||||
promises.all('resolved');
|
||||
});
|
||||
|
||||
test('State manager handles saveState() before loadState()', async () => {
|
||||
const key = 'key';
|
||||
const storage: any = {};
|
||||
|
||||
let getCount = 0;
|
||||
let getCallback: () => void;
|
||||
const resolveGet = () => getCallback();
|
||||
const get = (storageKey: string, callback: (data: any) => void) => {
|
||||
expect(storageKey).toEqual(key);
|
||||
getCount++;
|
||||
getCallback = () => {
|
||||
callback({[storageKey]: storage[storageKey]});
|
||||
};
|
||||
};
|
||||
|
||||
let setCount = 0;
|
||||
const set = () => setCount++;
|
||||
|
||||
const parent: any = {
|
||||
data: 'fromParent',
|
||||
};
|
||||
|
||||
const stateManager = new StateManagerImpl(key, parent, {
|
||||
data: 'fromDefault',
|
||||
}, {get, set}, noop, noop);
|
||||
|
||||
expect(parent).toEqual({
|
||||
data: 'fromParent',
|
||||
});
|
||||
|
||||
const promise = new PromiseWrapper();
|
||||
promise.add(stateManager.saveState());
|
||||
|
||||
expect(parent).toEqual({
|
||||
data: 'fromParent',
|
||||
});
|
||||
expect(getCount).toEqual(1);
|
||||
expect(setCount).toEqual(0);
|
||||
|
||||
await nextTick();
|
||||
promise.promises.forEach((state) => expect(state).toBe('pending'));
|
||||
|
||||
resolveGet();
|
||||
|
||||
await nextTick();
|
||||
promise.promises.forEach((state) => expect(state).toBe('resolved'));
|
||||
|
||||
expect(parent).toEqual({
|
||||
data: 'fromDefault',
|
||||
});
|
||||
|
||||
await stateManager.loadState();
|
||||
|
||||
expect(parent).toEqual({
|
||||
data: 'fromDefault',
|
||||
});
|
||||
|
||||
expect(setCount).toEqual(0);
|
||||
});
|
||||
|
||||
test('State manager handles interleaved saveState() and loadState()', async () => {
|
||||
const key = 'key';
|
||||
const storage: any = {};
|
||||
|
||||
let getCount = 0;
|
||||
let getCallback: () => void;
|
||||
const resolveGet = () => getCallback();
|
||||
const get = (storageKey: string, callback: (data: any) => void) => {
|
||||
expect(storageKey).toEqual(key);
|
||||
getCount++;
|
||||
getCallback = () => {
|
||||
callback({[storageKey]: storage[storageKey]});
|
||||
};
|
||||
};
|
||||
|
||||
let setCount = 0;
|
||||
let setCallback: () => void;
|
||||
const resolveSet = () => setCallback();
|
||||
const set = (items: any, callback: () => void) => {
|
||||
setCount++;
|
||||
setCallback = () => {
|
||||
Object.assign(storage, items);
|
||||
callback();
|
||||
};
|
||||
};
|
||||
|
||||
const parent: any = {
|
||||
data: 'fromParent',
|
||||
};
|
||||
|
||||
const stateManager = new StateManagerImpl(key, parent, {
|
||||
data: 'fromDefault',
|
||||
}, {get, set}, noop, noop);
|
||||
|
||||
expect(parent).toEqual({
|
||||
data: 'fromParent',
|
||||
});
|
||||
|
||||
const promises = new PromiseWrapper();
|
||||
promises.add([
|
||||
stateManager.loadState(),
|
||||
stateManager.loadState(),
|
||||
stateManager.saveState(),
|
||||
stateManager.loadState(),
|
||||
stateManager.loadState(),
|
||||
stateManager.saveState(),
|
||||
stateManager.saveState(),
|
||||
stateManager.saveState(),
|
||||
]);
|
||||
|
||||
expect(parent).toEqual({
|
||||
data: 'fromParent',
|
||||
});
|
||||
expect(getCount).toEqual(1);
|
||||
expect(setCount).toEqual(0);
|
||||
|
||||
await nextTick();
|
||||
expect(getCount).toEqual(1);
|
||||
expect(setCount).toEqual(0);
|
||||
promises.all('pending');
|
||||
|
||||
resolveGet();
|
||||
|
||||
await nextTick();
|
||||
expect(parent).toEqual({
|
||||
data: 'fromDefault',
|
||||
});
|
||||
expect(getCount).toEqual(1);
|
||||
expect(setCount).toEqual(0);
|
||||
promises.all('resolved');
|
||||
|
||||
parent.data = 'new';
|
||||
await stateManager.loadState();
|
||||
expect(parent).toEqual({
|
||||
data: 'new',
|
||||
});
|
||||
expect(getCount).toEqual(1);
|
||||
expect(setCount).toEqual(0);
|
||||
|
||||
const promises2 = new PromiseWrapper();
|
||||
promises2.add([
|
||||
stateManager.saveState(),
|
||||
stateManager.loadState(),
|
||||
stateManager.saveState(),
|
||||
stateManager.loadState(),
|
||||
stateManager.loadState(),
|
||||
stateManager.saveState(),
|
||||
stateManager.loadState(),
|
||||
stateManager.saveState(),
|
||||
]);
|
||||
|
||||
expect(getCount).toEqual(1);
|
||||
expect(setCount).toEqual(1);
|
||||
|
||||
expect(parent).toEqual({
|
||||
data: 'new',
|
||||
});
|
||||
|
||||
resolveSet();
|
||||
|
||||
await nextTick();
|
||||
expect(parent).toEqual({
|
||||
data: 'new',
|
||||
});
|
||||
expect(getCount).toEqual(1);
|
||||
expect(setCount).toEqual(2);
|
||||
|
||||
resolveSet();
|
||||
|
||||
await nextTick();
|
||||
promises2.all('resolved');
|
||||
});
|
||||
|
||||
test('State manager handles onChanged during saveState() and loadState()', async () => {
|
||||
const key = 'key';
|
||||
const storage: any = {};
|
||||
|
||||
let getCount = 0;
|
||||
let getCallback: (() => void) | undefined;
|
||||
const resolveGet = () => {
|
||||
getCallback!();
|
||||
getCallback = undefined;
|
||||
};
|
||||
|
||||
const get = (storageKey: string, callback: (data: any) => void) => {
|
||||
expect(storageKey).toEqual(key);
|
||||
getCount++;
|
||||
getCallback = () => {
|
||||
callback({[storageKey]: storage[storageKey]});
|
||||
};
|
||||
};
|
||||
|
||||
let setCount = 0;
|
||||
let setCallback: (() => void) | undefined;
|
||||
const resolveSet = () => {
|
||||
setCallback!();
|
||||
setCallback = undefined;
|
||||
};
|
||||
|
||||
const set = (items: any, callback: () => void) => {
|
||||
setCount++;
|
||||
setCallback = () => {
|
||||
Object.assign(storage, items);
|
||||
callback();
|
||||
};
|
||||
};
|
||||
|
||||
let onChangedListener: ((data: any) => void) | undefined;
|
||||
const modifyInternalState = (data: any) => {
|
||||
expect(onChangedListener).toBeTruthy();
|
||||
const oldValue = storage[key];
|
||||
storage[key] = data;
|
||||
onChangedListener!({
|
||||
[key]: {
|
||||
oldValue,
|
||||
newValue: data,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const parent: any = {
|
||||
data: 'fromParent',
|
||||
};
|
||||
|
||||
const stateManager = new StateManagerImpl(key, parent, {
|
||||
data: 'fromDefault',
|
||||
}, {get, set}, (listener) => onChangedListener = listener, noop);
|
||||
|
||||
const c1 = jest.fn();
|
||||
stateManager.addChangeListener(c1);
|
||||
|
||||
expect(parent).toEqual({
|
||||
data: 'fromParent',
|
||||
});
|
||||
|
||||
const promises = new PromiseWrapper();
|
||||
promises.add(stateManager.loadState());
|
||||
|
||||
expect(parent).toEqual({
|
||||
data: 'fromParent',
|
||||
});
|
||||
expect(getCount).toEqual(1);
|
||||
expect(setCount).toEqual(0);
|
||||
|
||||
await nextTick();
|
||||
expect(getCount).toEqual(1);
|
||||
expect(setCount).toEqual(0);
|
||||
promises.all('pending');
|
||||
|
||||
expect(c1).not.toHaveBeenCalled();
|
||||
|
||||
modifyInternalState({
|
||||
data: 'fromStorageChange',
|
||||
});
|
||||
|
||||
resolveGet();
|
||||
|
||||
await nextTick();
|
||||
expect(c1).toHaveBeenCalled();
|
||||
expect(parent).toEqual({
|
||||
data: 'fromStorageChange',
|
||||
});
|
||||
expect(getCount).toEqual(2);
|
||||
expect(setCount).toEqual(0);
|
||||
promises.all('resolved');
|
||||
|
||||
expect(stateManager.getStateForTesting()).toEqual('Ready');
|
||||
|
||||
const c2 = jest.fn();
|
||||
stateManager.addChangeListener(c2);
|
||||
|
||||
parent.data = 'new';
|
||||
await stateManager.loadState();
|
||||
expect(parent).toEqual({
|
||||
data: 'new',
|
||||
});
|
||||
expect(getCount).toEqual(2);
|
||||
expect(setCount).toEqual(0);
|
||||
expect(stateManager.getStateForTesting()).toEqual('Ready');
|
||||
|
||||
const promises2 = new PromiseWrapper;
|
||||
promises2.add(stateManager.saveState());
|
||||
|
||||
modifyInternalState({
|
||||
data: 'fromStorageChange2',
|
||||
});
|
||||
expect(getCount).toEqual(2);
|
||||
expect(setCount).toEqual(1);
|
||||
promises2.all('pending');
|
||||
|
||||
// During data race the JS-world data does not get overwriten
|
||||
expect(parent).toEqual({
|
||||
data: 'new',
|
||||
});
|
||||
|
||||
resolveSet();
|
||||
|
||||
await nextTick();
|
||||
expect(parent).toEqual({
|
||||
data: 'new',
|
||||
});
|
||||
expect(getCount).toEqual(3);
|
||||
expect(setCount).toEqual(1);
|
||||
promises2.all('pending');
|
||||
|
||||
expect(c2).not.toHaveBeenCalled();
|
||||
|
||||
resolveGet();
|
||||
|
||||
await nextTick();
|
||||
expect(c2).toHaveBeenCalled();
|
||||
expect(parent).toEqual({
|
||||
data: 'new',
|
||||
});
|
||||
expect(getCount).toEqual(3);
|
||||
expect(setCount).toEqual(1);
|
||||
promises2.all('resolved');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,112 @@
|
||||
import {formatCSS} from '../../../src/utils/css-text/format-css';
|
||||
import {getParenthesesRange} from '../../../src/utils/text';
|
||||
|
||||
test('CSS formatting', () => {
|
||||
expect(formatCSS('div { color: red; }'))
|
||||
.toEqual([
|
||||
'div {',
|
||||
' color: red;',
|
||||
'}',
|
||||
].join('\n'));
|
||||
|
||||
expect(formatCSS('div { color: red; } .list-item { background: rgb(0, 0, 0); color: white; }'))
|
||||
.toEqual([
|
||||
'div {',
|
||||
' color: red;',
|
||||
'}',
|
||||
'.list-item {',
|
||||
' background: rgb(0, 0, 0);',
|
||||
' color: white;',
|
||||
'}',
|
||||
].join('\n'));
|
||||
|
||||
expect(formatCSS('@media screen { div { color: red; } span { color: red; } } @media all { p { color: green; } }'))
|
||||
.toEqual([
|
||||
'@media screen {',
|
||||
' div {',
|
||||
' color: red;',
|
||||
' }',
|
||||
' span {',
|
||||
' color: red;',
|
||||
' }',
|
||||
'}',
|
||||
'@media all {',
|
||||
' p {',
|
||||
' color: green;',
|
||||
' }',
|
||||
'}',
|
||||
].join('\n'));
|
||||
|
||||
expect(formatCSS('div, span { background: green; color: red; }'))
|
||||
.toEqual([
|
||||
'div,',
|
||||
'span {',
|
||||
' background: green;',
|
||||
' color: red;',
|
||||
'}',
|
||||
].join('\n'));
|
||||
|
||||
expect(formatCSS('@media print, screen and (min-width: 20rem) { div, span { background: green; color: red; } }'))
|
||||
.toEqual([
|
||||
'@media print, screen and (min-width: 20rem) {',
|
||||
' div,',
|
||||
' span {',
|
||||
' background: green;',
|
||||
' color: red;',
|
||||
' }',
|
||||
'}',
|
||||
].join('\n'));
|
||||
|
||||
expect(formatCSS('.icon { background-image: url(data:image/gif;base64,XYZ); }'))
|
||||
.toEqual([
|
||||
'.icon {',
|
||||
' background-image: url(data:image/gif;base64,XYZ);',
|
||||
'}',
|
||||
].join('\n'));
|
||||
|
||||
expect(formatCSS('img[src*="a,b;c"] { filter: invert(1); }'))
|
||||
.toEqual([
|
||||
'img[src*="a,b;c"] {',
|
||||
' filter: invert(1);',
|
||||
'}',
|
||||
].join('\n'));
|
||||
|
||||
expect(formatCSS('img[src*="a,b;c"] {\n filter: invert(1); \n}'))
|
||||
.toEqual([
|
||||
'img[src*="a,b;c"] {',
|
||||
' filter: invert(1);',
|
||||
'}',
|
||||
].join('\n'));
|
||||
|
||||
expect(formatCSS('div { } span { color: red; } button { }'))
|
||||
.toEqual([
|
||||
'span {',
|
||||
' color: red;',
|
||||
'}',
|
||||
].join('\n'));
|
||||
|
||||
expect(formatCSS('@media all { div { } span { color: red; } } @media all { div { } } @media all { button { color: green; } }'))
|
||||
.toEqual([
|
||||
'@media all {',
|
||||
' span {',
|
||||
' color: red;',
|
||||
' }',
|
||||
'}',
|
||||
'@media all {',
|
||||
' button {',
|
||||
' color: green;',
|
||||
' }',
|
||||
'}',
|
||||
].join('\n'));
|
||||
});
|
||||
|
||||
test('Parenthesis Range', () => {
|
||||
expect(getParenthesesRange('missing')).toBe(null);
|
||||
expect(getParenthesesRange('()')).toEqual({start: 0, end: 2});
|
||||
expect(getParenthesesRange('rgb(0, 0, 0)')).toEqual({start: 3, end: 12});
|
||||
expect(getParenthesesRange('rgb(0, 0, 0), rgb(0, 0, 0)')).toEqual({start: 3, end: 12});
|
||||
expect(getParenthesesRange('rgb(0, 0, 0), rgb(0, 0, 0)', 12)).toEqual({start: 17, end: 26});
|
||||
expect(getParenthesesRange('rgb(0, var(--x, var(--y)), 0)')).toEqual({start: 3, end: 29});
|
||||
expect(getParenthesesRange('rgb(0, var(--x, var(--y)), 0)', 4)).toEqual({start: 10, end: 25});
|
||||
expect(getParenthesesRange('rgb(0, var(--x, var(--y)), 0), rgb(0, 0, 0)')).toEqual({start: 3, end: 29});
|
||||
});
|
||||
@@ -0,0 +1,128 @@
|
||||
import {isInTimeIntervalLocal, nextTimeInterval, isNightAtLocation, nextTimeChangeAtLocation, parseTime, getDuration, getDurationInMinutes} from '../../../src/utils/time';
|
||||
|
||||
test('Time interval', () => {
|
||||
// isInTimeIntervalLocal is time-zone dependent
|
||||
expect(isInTimeIntervalLocal('9:00', '12:00', new Date(2018, 11, 4, 10))).toEqual(true);
|
||||
expect(isInTimeIntervalLocal('10:00', '10:00', new Date(2018, 11, 4, 10))).toEqual(false);
|
||||
expect(isInTimeIntervalLocal('10:00', '10:00', new Date(2018, 11, 4, 12))).toEqual(false);
|
||||
expect(isInTimeIntervalLocal('10:00', '10:00', new Date(2018, 11, 4, 8))).toEqual(false);
|
||||
expect(isInTimeIntervalLocal('9:00', '10:00', new Date(2018, 11, 4, 10))).toEqual(false);
|
||||
expect(isInTimeIntervalLocal('9:01', '10:00', new Date(2018, 11, 4, 9, 2))).toEqual(true);
|
||||
expect(isInTimeIntervalLocal('9:00', '10:01', new Date(2018, 11, 4, 10))).toEqual(true);
|
||||
expect(isInTimeIntervalLocal('18:00', '12:00', new Date(2018, 11, 4, 10))).toEqual(true);
|
||||
expect(isInTimeIntervalLocal('18:00', '9:00', new Date(2018, 11, 4, 10))).toEqual(false);
|
||||
expect(isInTimeIntervalLocal('18:00', '9:00', new Date(2018, 11, 4, 22))).toEqual(true);
|
||||
});
|
||||
|
||||
test('Time interval prediction', () => {
|
||||
// nextTimeInterval() returns time-zone dependent timestamp
|
||||
expect(nextTimeInterval('9:00', '12:00', new Date(2018, 11, 4, 10))).toEqual(new Date(2018, 11, 4, 12).getTime());
|
||||
expect(nextTimeInterval('10:00', '10:00', new Date(2018, 11, 4, 10))).toEqual(null);
|
||||
expect(nextTimeInterval('10:00', '10:00', new Date(2018, 11, 4, 12))).toEqual(null);
|
||||
expect(nextTimeInterval('10:00', '10:00', new Date(2018, 11, 4, 8))).toEqual(null);
|
||||
expect(nextTimeInterval('9:00', '10:00', new Date(2018, 11, 4, 10))).toEqual(new Date(2018, 11, 5, 9).getTime());
|
||||
expect(nextTimeInterval('9:01', '10:00', new Date(2018, 11, 4, 9, 2))).toEqual(new Date(2018, 11, 4, 10).getTime());
|
||||
expect(nextTimeInterval('9:00', '10:01', new Date(2018, 11, 4, 10))).toEqual(new Date(2018, 11, 4, 10, 1).getTime());
|
||||
expect(nextTimeInterval('18:00', '12:00', new Date(2018, 11, 4, 10))).toEqual(new Date(2018, 11, 4, 12).getTime());
|
||||
expect(nextTimeInterval('18:00', '9:00', new Date(2018, 11, 4, 10))).toEqual(new Date(2018, 11, 4, 18).getTime());
|
||||
expect(nextTimeInterval('18:00', '9:00', new Date(2018, 11, 4, 22))).toEqual(new Date(2018, 11, 5, 9).getTime());
|
||||
});
|
||||
|
||||
test('Time parse', () => {
|
||||
expect(parseTime('10:30')).toEqual([10, 30]);
|
||||
expect(parseTime('10:30AM')).toEqual([10, 30]);
|
||||
expect(parseTime('10:30 a.m.')).toEqual([10, 30]);
|
||||
expect(parseTime('10:30PM')).toEqual([22, 30]);
|
||||
expect(parseTime('10:30 p.m.')).toEqual([22, 30]);
|
||||
expect(parseTime('0:30')).toEqual([0, 30]);
|
||||
expect(parseTime('12:30am')).toEqual([0, 30]);
|
||||
expect(parseTime('12:30pm')).toEqual([12, 30]);
|
||||
});
|
||||
|
||||
test('Duration', () => {
|
||||
expect(getDuration({
|
||||
seconds: 48,
|
||||
minutes: 24,
|
||||
hours: 8,
|
||||
days: 3,
|
||||
})).toEqual(289488000);
|
||||
|
||||
expect(getDurationInMinutes({
|
||||
seconds: 27,
|
||||
minutes: 25,
|
||||
hours: 5,
|
||||
days: 7,
|
||||
})).toEqual(10405.45);
|
||||
});
|
||||
|
||||
test('Nigth check', () => {
|
||||
const utcDate = (y: number, m: number, d: number, hh: number, mm: number) => new Date(Date.UTC(y, m, d, hh, mm));
|
||||
|
||||
expect(isNightAtLocation(52, 0, utcDate(2019, 8, 9, 0, 0))).toEqual(true);
|
||||
expect(isNightAtLocation(52, 0, utcDate(2019, 8, 9, 5, 0))).toEqual(true);
|
||||
expect(isNightAtLocation(52, 0, utcDate(2019, 8, 9, 7, 0))).toEqual(false);
|
||||
expect(isNightAtLocation(52, 0, utcDate(2019, 8, 9, 12, 0))).toEqual(false);
|
||||
expect(isNightAtLocation(52, 0, utcDate(2019, 8, 9, 18, 0))).toEqual(false);
|
||||
expect(isNightAtLocation(52, 0, utcDate(2019, 8, 9, 20, 0))).toEqual(true);
|
||||
expect(isNightAtLocation(52, 0, utcDate(2019, 8, 9, 23, 59))).toEqual(true);
|
||||
|
||||
expect(isNightAtLocation(52, 30, utcDate(2019, 8, 9, 0, 0))).toEqual(true);
|
||||
expect(isNightAtLocation(52, 30, utcDate(2019, 8, 9, 3, 0))).toEqual(true);
|
||||
expect(isNightAtLocation(52, 30, utcDate(2019, 8, 9, 5, 0))).toEqual(false);
|
||||
expect(isNightAtLocation(52, 30, utcDate(2019, 8, 9, 10, 0))).toEqual(false);
|
||||
expect(isNightAtLocation(52, 30, utcDate(2019, 8, 9, 16, 0))).toEqual(false);
|
||||
expect(isNightAtLocation(52, 30, utcDate(2019, 8, 9, 18, 0))).toEqual(true);
|
||||
expect(isNightAtLocation(52, 30, utcDate(2019, 8, 9, 23, 59))).toEqual(true);
|
||||
|
||||
expect(isNightAtLocation(52, -30, utcDate(2019, 8, 9, 0, 0))).toEqual(true);
|
||||
expect(isNightAtLocation(52, -30, utcDate(2019, 8, 9, 7, 0))).toEqual(true);
|
||||
expect(isNightAtLocation(52, -30, utcDate(2019, 8, 9, 9, 0))).toEqual(false);
|
||||
expect(isNightAtLocation(52, -30, utcDate(2019, 8, 9, 14, 0))).toEqual(false);
|
||||
expect(isNightAtLocation(52, -30, utcDate(2019, 8, 9, 20, 0))).toEqual(false);
|
||||
expect(isNightAtLocation(52, -30, utcDate(2019, 8, 9, 22, 0))).toEqual(true);
|
||||
expect(isNightAtLocation(52, -30, utcDate(2019, 8, 9, 23, 59))).toEqual(true);
|
||||
|
||||
// Polar day and night
|
||||
expect(isNightAtLocation(71, 0, utcDate(2019, 5, 15, 0, 0))).toEqual(false);
|
||||
expect(isNightAtLocation(-71, 0, utcDate(2019, 5, 15, 0, 0))).toEqual(true);
|
||||
|
||||
// Places where sunset comes before sunrise (in UTC)
|
||||
expect(isNightAtLocation(0, 180, utcDate(2019, 8, 9, 0, 0))).toEqual(false);
|
||||
expect(isNightAtLocation(0, 180, utcDate(2019, 8, 9, 7, 0))).toEqual(true);
|
||||
});
|
||||
|
||||
test('Sunrise/sunset', () => {
|
||||
const utcDate = (y: number, m: number, d: number, hh: number, mm: number) => new Date(Date.UTC(y, m, d, hh, mm));
|
||||
|
||||
expect(nextTimeChangeAtLocation(52, 0, utcDate(2019, 8, 9, 0, 0))).toEqual(Date.UTC(2019, 8, 9, 5, 24, 2, 501));
|
||||
expect(nextTimeChangeAtLocation(52, 0, utcDate(2019, 8, 9, 5, 0))).toEqual(Date.UTC(2019, 8, 9, 5, 24, 2, 501));
|
||||
expect(nextTimeChangeAtLocation(52, 0, utcDate(2019, 8, 9, 7, 0))).toEqual(Date.UTC(2019, 8, 9, 18, 29, 46, 448));
|
||||
expect(nextTimeChangeAtLocation(52, 0, utcDate(2019, 8, 9, 12, 0))).toEqual(Date.UTC(2019, 8, 9, 18, 29, 46, 448));
|
||||
expect(nextTimeChangeAtLocation(52, 0, utcDate(2019, 8, 9, 18, 0))).toEqual(Date.UTC(2019, 8, 9, 18, 29, 46, 448));
|
||||
expect(nextTimeChangeAtLocation(52, 0, utcDate(2019, 8, 9, 20, 0))).toEqual(Date.UTC(2019, 8, 10, 5, 24, 2, 501));
|
||||
expect(nextTimeChangeAtLocation(52, 0, utcDate(2019, 8, 9, 23, 59))).toEqual(Date.UTC(2019, 8, 10, 5, 24, 2, 501));
|
||||
|
||||
expect(nextTimeChangeAtLocation(52, 30, utcDate(2019, 8, 9, 0, 0))).toEqual(Date.UTC(2019, 8, 9, 3, 23, 54, 365));
|
||||
expect(nextTimeChangeAtLocation(52, 30, utcDate(2019, 8, 9, 3, 0))).toEqual(Date.UTC(2019, 8, 9, 3, 23, 54, 365));
|
||||
expect(nextTimeChangeAtLocation(52, 30, utcDate(2019, 8, 9, 5, 0))).toEqual(Date.UTC(2019, 8, 9, 16, 29, 58, 45));
|
||||
expect(nextTimeChangeAtLocation(52, 30, utcDate(2019, 8, 9, 10, 0))).toEqual(Date.UTC(2019, 8, 9, 16, 29, 58, 45));
|
||||
expect(nextTimeChangeAtLocation(52, 30, utcDate(2019, 8, 9, 16, 0))).toEqual(Date.UTC(2019, 8, 9, 16, 29, 58, 45));
|
||||
expect(nextTimeChangeAtLocation(52, 30, utcDate(2019, 8, 9, 18, 0))).toEqual(Date.UTC(2019, 8, 10, 3, 23, 54, 365));
|
||||
expect(nextTimeChangeAtLocation(52, 30, utcDate(2019, 8, 9, 23, 59))).toEqual(Date.UTC(2019, 8, 10, 3, 23, 54, 365));
|
||||
|
||||
expect(nextTimeChangeAtLocation(52, -30, utcDate(2019, 8, 9, 0, 0))).toEqual(Date.UTC(2019, 8, 9, 7, 24, 10, 637));
|
||||
expect(nextTimeChangeAtLocation(52, -30, utcDate(2019, 8, 9, 7, 0))).toEqual(Date.UTC(2019, 8, 9, 7, 24, 10, 637));
|
||||
expect(nextTimeChangeAtLocation(52, -30, utcDate(2019, 8, 9, 9, 0))).toEqual(Date.UTC(2019, 8, 9, 20, 29, 34, 848));
|
||||
expect(nextTimeChangeAtLocation(52, -30, utcDate(2019, 8, 9, 14, 0))).toEqual(Date.UTC(2019, 8, 9, 20, 29, 34, 848));
|
||||
expect(nextTimeChangeAtLocation(52, -30, utcDate(2019, 8, 9, 20, 0))).toEqual(Date.UTC(2019, 8, 9, 20, 29, 34, 848));
|
||||
expect(nextTimeChangeAtLocation(52, -30, utcDate(2019, 8, 9, 22, 0))).toEqual(Date.UTC(2019, 8, 10, 7, 24, 10, 637));
|
||||
expect(nextTimeChangeAtLocation(52, -30, utcDate(2019, 8, 9, 23, 59))).toEqual(Date.UTC(2019, 8, 10, 7, 24, 10, 637));
|
||||
|
||||
// Polar day and night
|
||||
expect(nextTimeChangeAtLocation(71, 0, utcDate(2019, 5, 15, 0, 0))).toEqual(Date.UTC(2019, 5, 16, 0, 0, 0, 0));
|
||||
expect(nextTimeChangeAtLocation(-71, 0, utcDate(2019, 5, 15, 0, 0))).toEqual(Date.UTC(2019, 5, 16, 0, 0, 0, 0));
|
||||
|
||||
// Places where sunset comes before sunrise (in UTC)
|
||||
expect(nextTimeChangeAtLocation(0, 180, utcDate(2019, 8, 9, 0, 0))).toEqual(Date.UTC(2019, 8, 9, 6, 0, 50, 152));
|
||||
expect(nextTimeChangeAtLocation(0, 180, utcDate(2019, 8, 9, 7, 0))).toEqual(Date.UTC(2019, 8, 9, 17, 54, 18, 610));
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
import {randomFillSync} from 'crypto';
|
||||
|
||||
import {generateUID} from '../../../src/utils/uid';
|
||||
|
||||
test('Unique identifier generation (popyfilled)', () => {
|
||||
if ('crypto' in globalThis) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we are not messing up global context for somebody else
|
||||
expect('crypto' in globalThis).toEqual(false);
|
||||
|
||||
// Create a shim for crypto API
|
||||
// TODO: remove any cast once type declarations are updated
|
||||
const shim1 = jest.fn();
|
||||
globalThis.crypto = {
|
||||
getRandomValues: (buffer: Uint8Array) => {
|
||||
shim1();
|
||||
return randomFillSync(buffer);
|
||||
},
|
||||
} as any;
|
||||
const uid1 = generateUID();
|
||||
expect(/^[a-f0-9]{32}$/.test(uid1)).toEqual(true);
|
||||
expect(shim1).toHaveBeenCalled();
|
||||
|
||||
const shim2 = jest.fn();
|
||||
globalThis.crypto = {
|
||||
randomUUID: () => {
|
||||
shim2();
|
||||
return 'a19cc926-bf7f-4d5f-bf9c-202bc7a4c7c6';
|
||||
},
|
||||
} as any;
|
||||
expect(generateUID()).toEqual('a19cc926bf7f4d5fbf9c202bc7a4c7c6');
|
||||
expect(shim2).toHaveBeenCalled();
|
||||
|
||||
delete (globalThis as any).crypto;
|
||||
});
|
||||
|
||||
test('Unique identifier generation (NodeJS 19+)', () => {
|
||||
if (!('crypto' in globalThis)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure crypto.randomUUID() is actually supported
|
||||
expect('crypto' in globalThis && 'randomUUID' in crypto).toEqual(true);
|
||||
|
||||
const uid1 = generateUID();
|
||||
expect(/^[a-f0-9]{32}$/.test(uid1)).toEqual(true);
|
||||
});
|
||||
@@ -0,0 +1,146 @@
|
||||
import {indexURLTemplateList, isPDF, isURLInIndexedList, isURLMatched} from '../../../src/utils/url';
|
||||
|
||||
describe('Domain utilities', () => {
|
||||
test('URL match', () => {
|
||||
expect(isURLMatched('https://www.example.com/', '*')).toEqual(true);
|
||||
expect(isURLMatched('https://www.example.com/', '*.*')).toEqual(true);
|
||||
expect(isURLMatched('https://www.example.com/', '*.*.*')).toEqual(true);
|
||||
expect(isURLMatched('https://www.example.com/', '*.*.*.*')).toEqual(false);
|
||||
|
||||
expect(isURLMatched('https://www.example.com/page/1', 'example.com')).toEqual(true);
|
||||
expect(isURLMatched('https://www.failure.com/page/1', 'example.com')).toEqual(false);
|
||||
expect(isURLMatched('https://xyz.example.com/page/1', 'example.com')).toEqual(false);
|
||||
expect(isURLMatched('https://xyz.www.example.com/page/1', 'example.com')).toEqual(false);
|
||||
|
||||
expect(isURLMatched('https://xyz.example.com/page/1', '*.example.com')).toEqual(true);
|
||||
expect(isURLMatched('https://abc.xyz.example.com/page/1', '*.example.com')).toEqual(true);
|
||||
expect(isURLMatched('https://xyz.failure.com/page/1', '*.example.com')).toEqual(false);
|
||||
|
||||
expect(isURLMatched('https://www.example.com/page/1', 'example.com/page')).toEqual(true);
|
||||
expect(isURLMatched('https://www.example.com/fail/1', 'example.com/page')).toEqual(false);
|
||||
|
||||
expect(isURLMatched('https://example.com/page/1', '^example.com')).toEqual(true);
|
||||
expect(isURLMatched('https://www.example.com/page/1', '^example.com')).toEqual(false);
|
||||
|
||||
expect(isURLMatched('https://www.example.com/', 'example.com$')).toEqual(true);
|
||||
expect(isURLMatched('https://www.example.com/page/1', 'example.com$')).toEqual(false);
|
||||
|
||||
expect(isURLMatched('https://www.example.de/', 'example.*')).toEqual(true);
|
||||
expect(isURLMatched('https://www.failure.com/', 'example.*')).toEqual(false);
|
||||
|
||||
expect(isURLMatched('https://www.example.co.uk/', 'example.*.*')).toEqual(true);
|
||||
expect(isURLMatched('https://www.example.com/', 'example.*.*')).toEqual(false);
|
||||
|
||||
expect(isURLMatched('https://www.example.com/path/long/enough', 'example.com/path/*')).toEqual(true);
|
||||
expect(isURLMatched('https://www.failure.com/fail/long/enough', 'example.com/path/*')).toEqual(false);
|
||||
|
||||
expect(isURLMatched('http://localhost:8080/', 'localhost:8080')).toEqual(true);
|
||||
expect(isURLMatched('http://localhost:1024/', 'localhost:8080')).toEqual(false);
|
||||
|
||||
expect(isURLMatched('http://localhost:8080/', 'localhost:*')).toEqual(true);
|
||||
expect(isURLMatched('http://172.168.0.100:8080/', 'localhost:*')).toEqual(false);
|
||||
|
||||
expect(isURLMatched('http://www.example.com/page/1', 'http://*')).toEqual(true);
|
||||
expect(isURLMatched('https://www.example.com/page/1', 'http://*')).toEqual(false);
|
||||
|
||||
expect(isURLMatched('file:///C:/My%20Documents/balance.xlsx', 'file:///C:')).toEqual(true);
|
||||
expect(isURLMatched('file:///D:/Bin/cat.gif', 'file:///C:')).toEqual(false);
|
||||
|
||||
expect(isURLMatched('https://www.example.com/page/1', '/www\.ex.*\.com/')).toEqual(true);
|
||||
expect(isURLMatched('https://www.failure.com/page/1', '/www\.ex.*\.com/')).toEqual(false);
|
||||
|
||||
expect(isURLMatched('https://[2001:0DB8:AC10:FE01::200E]/', '[2001:0DB8:AC10:FE01::200E]')).toEqual(true);
|
||||
expect(isURLMatched('https://[2001:0DB8:AC10:FE02::200E]/', '[2001:0DB8:AC10:FE01::200E]')).toEqual(false);
|
||||
expect(isURLMatched('https://[2001:0DB8:AC10:FE01::200E]:8080/', '[2001:0DB8:AC10:FE01::200E]:8080')).toEqual(true);
|
||||
expect(isURLMatched('https://[2001:0DB8:AC10:FE02::200E]:1024/', '[2001:0DB8:AC10:FE01::200E]:8080')).toEqual(false);
|
||||
});
|
||||
|
||||
test('URL is PDF', () => {
|
||||
expect(isPDF('https://www.google.com/file.pdf')).toBe(true);
|
||||
expect(isPDF('https://www.google.com/file.pdf?id=2')).toBe(true);
|
||||
expect(isPDF('https://www.google.com/file.pdf/resource')).toBe(false);
|
||||
expect(isPDF('https://www.google.com/resource?file=file.pdf')).toBe(false);
|
||||
expect(isPDF('https://www.google.com/very/good/hidden/folder/pdf#file.pdf')).toBe(false);
|
||||
expect(isPDF('https://fi.wikipedia.org/wiki/Tiedosto:ExtIPA_chart_(2015).pdf?uselang=en')).toBe(false);
|
||||
expect(isPDF('https://commons.wikimedia.org/wiki/File:ExtIPA_chart_(2015).pdf')).toBe(false);
|
||||
expect(isPDF('https://upload.wikimedia.org/wikipedia/commons/5/56/ExtIPA_chart_(2015).pdf')).toBe(true);
|
||||
});
|
||||
|
||||
test('URL list index', () => {
|
||||
const simplePatterns = [
|
||||
'apple.com',
|
||||
'google.com',
|
||||
];
|
||||
let indexed = indexURLTemplateList(simplePatterns);
|
||||
expect(isURLInIndexedList('https://apple.com/', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://www.apple.com/', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://google.com/', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://www.google.com/', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://www.google.co.uk/', indexed)).toEqual(false);
|
||||
expect(isURLInIndexedList('https://www.google.com/maps', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://mail.google.com/', indexed)).toEqual(false);
|
||||
expect(isURLInIndexedList('https://www.example.com/', indexed)).toEqual(false);
|
||||
|
||||
const wildcardPatterns = [
|
||||
'apple.com',
|
||||
'google.*',
|
||||
'*.example.com',
|
||||
];
|
||||
indexed = indexURLTemplateList(wildcardPatterns);
|
||||
expect(isURLInIndexedList('https://apple.com/', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://www.apple.com/', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://google.com/', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://www.google.com/', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://www.google.co.uk/', indexed)).toEqual(false);
|
||||
expect(isURLInIndexedList('https://www.google.com/maps', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://mail.google.com/', indexed)).toEqual(false);
|
||||
expect(isURLInIndexedList('https://example.com/', indexed)).toEqual(false);
|
||||
expect(isURLInIndexedList('https://www.example.com/', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://test.example.com/', indexed)).toEqual(true);
|
||||
|
||||
const pathPatterns = [
|
||||
'apple.com',
|
||||
'google.*/maps',
|
||||
];
|
||||
indexed = indexURLTemplateList(pathPatterns);
|
||||
expect(isURLInIndexedList('https://apple.com/', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://www.apple.com/', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://google.com/', indexed)).toEqual(false);
|
||||
expect(isURLInIndexedList('https://www.google.com/', indexed)).toEqual(false);
|
||||
expect(isURLInIndexedList('https://www.google.co.uk/', indexed)).toEqual(false);
|
||||
expect(isURLInIndexedList('https://www.google.com/maps', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://www.google.com/maps/edit', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://www.google.co.uk/maps', indexed)).toEqual(false);
|
||||
expect(isURLInIndexedList('https://www.google.com/mail', indexed)).toEqual(false);
|
||||
expect(isURLInIndexedList('https://mail.google.com/', indexed)).toEqual(false);
|
||||
expect(isURLInIndexedList('https://www.example.com/', indexed)).toEqual(false);
|
||||
|
||||
const mixedPatterns = [
|
||||
'apple.com',
|
||||
'google.*/maps',
|
||||
'google.*.*/maps',
|
||||
'*.example.com',
|
||||
'office.com/*/edit',
|
||||
'mail.google.*/mail',
|
||||
];
|
||||
indexed = indexURLTemplateList(mixedPatterns);
|
||||
expect(isURLInIndexedList('https://apple.com/', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://www.apple.com/', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://google.com/', indexed)).toEqual(false);
|
||||
expect(isURLInIndexedList('https://www.google.com/', indexed)).toEqual(false);
|
||||
expect(isURLInIndexedList('https://www.google.co.uk/', indexed)).toEqual(false);
|
||||
expect(isURLInIndexedList('https://www.google.com/maps', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://www.google.co.uk/maps', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://www.google.com/maps/edit', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://www.google.com/mail', indexed)).toEqual(false);
|
||||
expect(isURLInIndexedList('https://mail.google.com/', indexed)).toEqual(false);
|
||||
expect(isURLInIndexedList('https://mail.google.com/mail/u/0/', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://example.com/', indexed)).toEqual(false);
|
||||
expect(isURLInIndexedList('https://www.example.com/', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://test.example.com/', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://long.test.example.com/', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://www.office.com/excel/', indexed)).toEqual(false);
|
||||
expect(isURLInIndexedList('https://www.office.com/excel/edit', indexed)).toEqual(true);
|
||||
expect(isURLInIndexedList('https://www.office.com/excel/edit/2000', indexed)).toEqual(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,214 @@
|
||||
import {DEFAULT_SETTINGS, DEFAULT_THEME} from '../../../src/defaults';
|
||||
import type {Theme, UserSettings} from '../../../src/definitions';
|
||||
import {ThemeEngine} from '../../../src/generators/theme-engines';
|
||||
import {AutomationMode} from '../../../src/utils/automation';
|
||||
import {validateSettings, validateTheme} from '../../../src/utils/validation';
|
||||
|
||||
test('Settings Validation', () => {
|
||||
const defaultTheme = JSON.parse(JSON.stringify(DEFAULT_THEME));
|
||||
let themeValidation = validateTheme(defaultTheme);
|
||||
expect(themeValidation.errors).toEqual([]);
|
||||
expect(defaultTheme).toEqual(DEFAULT_THEME);
|
||||
|
||||
const partTheme = {
|
||||
brightness: 125,
|
||||
contrast: -50,
|
||||
};
|
||||
themeValidation = validateTheme(partTheme as any);
|
||||
expect(themeValidation.errors.length).toBe(1);
|
||||
expect(partTheme).toEqual({
|
||||
brightness: 125,
|
||||
contrast: DEFAULT_THEME.contrast,
|
||||
});
|
||||
|
||||
const wonkyTheme = {
|
||||
mode: 'dark',
|
||||
brightness: 250,
|
||||
contrast: -50,
|
||||
grayscale: false,
|
||||
sepia: 105,
|
||||
useFont: 'ok',
|
||||
fontFamily: '',
|
||||
textStroke: 2,
|
||||
engine: 'dymanic',
|
||||
stylesheet: 3,
|
||||
darkSchemeBackgroundColor: 'red',
|
||||
darkSchemeTextColor: '#abc12x',
|
||||
lightSchemeBackgroundColor: '',
|
||||
lightSchemeTextColor: '#ffffff00',
|
||||
scrollbarColor: false,
|
||||
selectionColor: 'green',
|
||||
styleSystemControls: null as boolean | null,
|
||||
lightColorScheme: '',
|
||||
darkColorScheme: false,
|
||||
immediateModify: 1,
|
||||
};
|
||||
themeValidation = validateTheme(wonkyTheme as any);
|
||||
expect(themeValidation.errors.length).toBeGreaterThan(0);
|
||||
expect(wonkyTheme as any).toEqual(DEFAULT_THEME);
|
||||
|
||||
const defaultSet = JSON.parse(JSON.stringify(DEFAULT_SETTINGS));
|
||||
let validation = validateSettings(defaultSet);
|
||||
expect(validation.errors).toEqual([]);
|
||||
expect(defaultSet).toEqual(DEFAULT_SETTINGS);
|
||||
|
||||
const wonkySet = {
|
||||
schemeVersion: 'x',
|
||||
enabled: '',
|
||||
fetchNews: null as boolean | null,
|
||||
theme: {
|
||||
mode: 'dark',
|
||||
brightness: 250,
|
||||
contrast: -50,
|
||||
grayscale: false,
|
||||
sepia: 105,
|
||||
useFont: 'ok',
|
||||
fontFamily: '',
|
||||
textStroke: 2,
|
||||
engine: 'dymanic',
|
||||
stylesheet: 3,
|
||||
darkSchemeBackgroundColor: 'red',
|
||||
darkSchemeTextColor: '#abc12x',
|
||||
lightSchemeBackgroundColor: '',
|
||||
lightSchemeTextColor: '#ffffff00',
|
||||
scrollbarColor: false,
|
||||
selectionColor: 'green',
|
||||
styleSystemControls: null as boolean | null,
|
||||
lightColorScheme: '',
|
||||
darkColorScheme: false,
|
||||
immediateModify: 1,
|
||||
},
|
||||
presets: [
|
||||
{id: '', name: 'P1', urls: ['a.com'], theme: {brightness: 100}},
|
||||
{id: 'p2', name: '', urls: ['a.com'], theme: {brightness: 100}},
|
||||
{id: 'p3', name: 'P3', urls: [], theme: {brightness: 100}},
|
||||
{id: 'p4', name: 'P4', urls: ['a.com'], theme: {brightness: -50}},
|
||||
{id: 'p5', name: 'P5', urls: ['a.com'], theme: {brightness: 100}},
|
||||
{id: 'p6', urls: ['a.com'], theme: {brightness: 100}},
|
||||
{id: 'p7', name: 'P7', urls: ['a.com'], theme: null},
|
||||
null,
|
||||
],
|
||||
customThemes: [
|
||||
{url: [] as any[], theme: {brightness: 100}},
|
||||
{url: [''], theme: {brightness: 100}},
|
||||
{url: ['a.com'], theme: {brightness: 100}},
|
||||
{url: ['a.com'], theme: {brightness: -50}},
|
||||
{url: ['a.com']},
|
||||
{url: ['a.com'], theme: null},
|
||||
null,
|
||||
],
|
||||
disabledFor: ['a.com', '', 'b.com'],
|
||||
enabledFor: {0: 'a.com', 1: 'b.com'},
|
||||
enabledByDefault: true,
|
||||
changeBrowserTheme: 1,
|
||||
syncSettings: null as boolean | null,
|
||||
syncSitesFixes: 0,
|
||||
automation: {
|
||||
enabled: false,
|
||||
behavior: 'OnOff',
|
||||
mode: '',
|
||||
},
|
||||
time: {
|
||||
activation: '10:00PM',
|
||||
deactivation: '19:00',
|
||||
},
|
||||
location: {
|
||||
latitude: 59,
|
||||
longitude: '5.3',
|
||||
},
|
||||
previewNewDesign: '',
|
||||
previewNewestDesign: 2,
|
||||
enableForPDF: null as boolean | null,
|
||||
enableForProtectedPages: 'ok',
|
||||
enableContextMenus: 'yes',
|
||||
detectDarkTheme: 'no',
|
||||
};
|
||||
validation = validateSettings(wonkySet as any);
|
||||
expect(validation.errors.length).toBeGreaterThan(0);
|
||||
expect(wonkySet as any).toEqual({
|
||||
...DEFAULT_SETTINGS,
|
||||
disabledFor: ['a.com', 'b.com'],
|
||||
presets: [{id: 'p5', name: 'P5', urls: ['a.com'], theme: {brightness: 100}}],
|
||||
customThemes: [{url: ['a.com'], theme: {brightness: 100}}],
|
||||
});
|
||||
|
||||
const nullishSet = {
|
||||
theme: null as any,
|
||||
presets: null as any,
|
||||
customThemes: null as any,
|
||||
time: null as any,
|
||||
location: null as any,
|
||||
};
|
||||
validation = validateSettings(nullishSet);
|
||||
expect(validation.errors.length).toBe(5);
|
||||
expect(nullishSet).toEqual({
|
||||
theme: DEFAULT_THEME,
|
||||
presets: DEFAULT_SETTINGS.presets,
|
||||
customThemes: DEFAULT_SETTINGS.customThemes,
|
||||
time: DEFAULT_SETTINGS.time,
|
||||
location: DEFAULT_SETTINGS.location,
|
||||
});
|
||||
|
||||
const validSet: UserSettings = {
|
||||
schemeVersion: 2,
|
||||
enabled: true,
|
||||
fetchNews: true,
|
||||
theme: {
|
||||
mode: 0,
|
||||
brightness: 125,
|
||||
contrast: 50,
|
||||
grayscale: 25,
|
||||
sepia: 5,
|
||||
useFont: true,
|
||||
fontFamily: 'Comic Sans',
|
||||
textStroke: 0.5,
|
||||
engine: ThemeEngine.dynamicTheme,
|
||||
stylesheet: '.x { color: red; }',
|
||||
darkSchemeBackgroundColor: '#abcdef',
|
||||
darkSchemeTextColor: '#abc123',
|
||||
lightSchemeBackgroundColor: '#ff0000',
|
||||
lightSchemeTextColor: '#ffffff',
|
||||
scrollbarColor: '',
|
||||
selectionColor: 'auto',
|
||||
styleSystemControls: false,
|
||||
lightColorScheme: 'Lightness',
|
||||
darkColorScheme: 'Darkness',
|
||||
immediateModify: true,
|
||||
},
|
||||
presets: [
|
||||
{id: 'p5', name: 'P5', urls: ['a.com'], theme: {brightness: 100} as Theme},
|
||||
],
|
||||
customThemes: [
|
||||
{url: ['a.com'], theme: {brightness: 100} as Theme},
|
||||
],
|
||||
disabledFor: ['a.com', 'b.com'],
|
||||
enabledFor: ['c.com'],
|
||||
enabledByDefault: false,
|
||||
changeBrowserTheme: true,
|
||||
syncSettings: false,
|
||||
syncSitesFixes: true,
|
||||
automation: {
|
||||
enabled: true,
|
||||
mode: AutomationMode.LOCATION,
|
||||
behavior: 'OnOff',
|
||||
},
|
||||
time: {
|
||||
activation: '18:00',
|
||||
deactivation: '7:00',
|
||||
},
|
||||
location: {
|
||||
latitude: 59,
|
||||
longitude: 53,
|
||||
},
|
||||
previewNewDesign: true,
|
||||
previewNewestDesign: false,
|
||||
enableForPDF: false,
|
||||
enableForProtectedPages: true,
|
||||
enableContextMenus: true,
|
||||
detectDarkTheme: true,
|
||||
};
|
||||
const validSetCopy = JSON.parse(JSON.stringify(validSet));
|
||||
validation = validateSettings(validSet);
|
||||
expect(validation.errors).toEqual([]);
|
||||
expect(validSet).toEqual(validSetCopy);
|
||||
});
|
||||
Reference in New Issue
Block a user