Skip to content

Commit

Permalink
Tests
Browse files Browse the repository at this point in the history
  • Loading branch information
slevithan committed Oct 29, 2024
1 parent 9bc8f3c commit ae76dae
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 31 deletions.
10 changes: 5 additions & 5 deletions dist/index.min.js

Large diffs are not rendered by default.

48 changes: 35 additions & 13 deletions spec/compile.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,50 @@ import {compile} from '../dist/index.mjs';

describe('compile', () => {
it('should return an object with pattern and flags properties', () => {
expect(Object.keys(compile('a'))).toEqual(['pattern', 'flags']);
expect(Object.keys(compile(''))).toEqual(['pattern', 'flags']);
});

it('should return an empty pattern string if given an empty string', () => {
it('should throw for non-string pattern', () => {
expect(() => compile()).toThrow();
for (const value of [undefined, null, 0, false, [], {}, /(?:)/]) {
expect(() => compile(value)).toThrow();
}
});

it('should return an empty pattern if given an empty string', () => {
expect(compile('').pattern).toBe('');
});

it('should accept supported flags', () => {
const compiled = compile('', 'imx');
expect(compiled.flags).toContain('i');
expect(compiled.flags).toContain('s');
expect(compiled.flags).toContain('v');
// TODO: More specs
it('should accept and translate supported flags', () => {
expect(compile('', 'i').flags).toContain('i');
expect(compile('', 'm').flags).toContain('s');
expect(compile('', 'm').flags).not.toContain('m');
expect(compile('', 'x').flags).not.toContain('x');
});

it('should throw for unexpected flags', () => {
expect(() => compile('', 'd')).toThrow();
expect(() => compile('', 'g')).toThrow();
expect(() => compile('', 's')).toThrow();
expect(() => compile('', 'u')).toThrow();
expect(() => compile('', 'v')).toThrow();
expect(() => compile('', 'y')).toThrow();
});

it('should add flag v if target unspecified', () => {
expect(compile('').flags).toBe('v');
});

it('should add flag v for target ES2024+', () => {
expect(compile('', '', {target: 'ES2024'}).flags).toBe('v');
expect(compile('', '', {target: 'ESNext'}).flags).toBe('v');
});

it('should accept supported targets', () => {
expect(compile('', '', {target: 'ES2018'})).toEqual({pattern: '', flags: 'u'});
expect(compile('', '', {target: 'ES2024'})).toEqual({pattern: '', flags: 'v'});
expect(compile('', '', {target: 'ESNext'})).toEqual({pattern: '', flags: 'v'});
it('should add flag u for target ES2018', () => {
expect(compile('', '', {target: 'ES2018'}).flags).toBe('u');
});

it('should throw for unsupported targets', () => {
it('should throw for unexpected targets', () => {
expect(() => compile('', '', {target: 'ES6'})).toThrow();
expect(() => compile('', '', {target: 'ES2019'})).toThrow();
});
Expand Down
39 changes: 28 additions & 11 deletions spec/match-recursion.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,30 @@ beforeEach(() => {
});

describe('Recursion', () => {
it('should throw if recursion used with allowBestEffort false', () => {
expect(() => compile(r`a\g<0>?`, '', {allowBestEffort: false})).toThrow();
expect(() => compile('', '', {allowBestEffort: false})).not.toThrow();
});

it('should throw if recursion used with maxRecursionDepth null', () => {
expect(() => compile(r`a\g<0>?`, '', {maxRecursionDepth: null})).toThrow();
expect(() => compile('', '', {maxRecursionDepth: null})).not.toThrow();
});

it('should throw if maxRecursionDepth is not null or a positive-integer in range 2-100', () => {
for (const value of [-2, 0, 1, 2.5, 101, Infinity, '2', '', undefined, NaN, false, [], {}]) {
expect(() => compile('', '', {maxRecursionDepth: value})).toThrow();
}
});

describe('global', () => {
it('should match an equal number of two different subpatterns', () => {
it('should match direct recursion', () => {
// Match an equal number of two different subpatterns
expect('aaabbb').toExactlyMatch(r`a\g<0>?b`);
expect('test aaaaaabbb').toFindMatch(r`a\g<0>?b`);
expect('aaabbb').toExactlyMatch(r`(?<n>a\g<0>?b)`);
});

it('should match balanced brackets', () => {
// Match balanced brackets
const pattern = r`<(?:[^<>]|\g<0>)*>`;
expect([
'<>', '<<>>', '<a<b<c>d>e>', '<<<<<<a>>>bc>>>',
Expand All @@ -24,7 +40,7 @@ describe('Recursion', () => {
).toEqual(['<balanced <<brackets>>>', '<>', '<<a>>', '<b>']);
});

it('should throw for multiple overlapping recursions', () => {
it('should throw for multiple direct, overlapping recursions', () => {
expect(() => compile(r`a\g<0>?\g<0>?`)).toThrow();
});

Expand All @@ -42,20 +58,21 @@ describe('Recursion', () => {
});

describe('relative numbered', () => {
it('should throw for relative 0', () => {
expect(() => compile(r`a\g<-0>?`)).toThrow();
expect(() => compile(r`a\g<+0>?`)).toThrow();
});

// Current limitation of `regex-recursion`
it('should throw for recursion by number', () => {
expect(() => compile(r`(a\g<-1>?)`)).toThrow();
expect(() => compile(r`(a\g<+1>(\g<-2>?))`)).toThrow();
});

it('should throw for relative 0', () => {
expect(() => compile(r`a\g<-0>?`)).toThrow();
expect(() => compile(r`a\g<+0>?`)).toThrow();
});
});

describe('named', () => {
it('should match an equal number of two different subpatterns', () => {
it('should match direct recursion', () => {
// Match an equal number of two different subpatterns as the entire string
expect('aaabbb').toExactlyMatch(r`\A(?<r>a\g<r>?b)\z`);
expect('aaabb').not.toFindMatch(r`\A(?<r>a\g<r>?b)\z`);
});
Expand All @@ -67,7 +84,7 @@ describe('Recursion', () => {

// Current limitation of `regex-recursion`
it('should throw for multiple direct, non-overlapping recursions', () => {
// TODO: `regex-recursion` has a bug so using `toRegExp` instead of `compile`
// TODO: `regex-recursion` has a bug and lets invalid JS syntax through so using `toRegExp` instead of `compile`
expect(() => toRegExp(r`(?<r1>a\g<r1>?)(?<r2>a\g<r2>?)`)).toThrow();
});

Expand Down
1 change: 0 additions & 1 deletion src/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ function generate(ast, options) {
result.options.disable.v = true;
result.options.unicodeSetsPlugin = null;
}
// TODO: Include `metadata` (add to type) about use of atomic groups, possessive quantifiers, and recursion, so those plugins can be skipped if not needed
return result;
}

Expand Down
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function toRegexAst(pattern, flags) {
}

/**
Transpiles an Oniguruma regex pattern and flags to a native JS RegExp.
Transpiles an Oniguruma regex pattern and flags and returns a native JS RegExp.
@param {string} pattern Oniguruma regex pattern.
@param {string} [flags] Any combination of Oniguruma flags `imx` and JS flags `dg`. Flag `m` is
equivalent to JS's flag `s`.
Expand Down
3 changes: 3 additions & 0 deletions src/tokenize.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ const charClassTokenRe = new RegExp(r`
@returns {TokenizerResult}
*/
function tokenize(pattern, flags = '') {
if (typeof pattern !== 'string') {
throw new Error('String expected as pattern');
}
if (!/^[imx]*$/.test(flags)) {
throw new Error(`Flags "${flags}" unsupported in Oniguruma`);
}
Expand Down

0 comments on commit ae76dae

Please sign in to comment.