diff --git a/.github/workflows/release-npm.yml b/.github/workflows/release-npm.yml index 0c6b29e42..819a8ff81 100644 --- a/.github/workflows/release-npm.yml +++ b/.github/workflows/release-npm.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: - node-version: '16' + node-version: '17' cache: 'npm' cache-dependency-path: javascript/package-lock.json - run: npm install-test diff --git a/.github/workflows/test-javascript.yml b/.github/workflows/test-javascript.yml index 827361bf4..ce5763ecd 100644 --- a/.github/workflows/test-javascript.yml +++ b/.github/workflows/test-javascript.yml @@ -14,12 +14,13 @@ jobs: matrix: os: - ubuntu-latest - node-version: ['12.x', '14.x', '16.x'] + # 16.12.0 has broken ESM support + node-version: ['12.x', '14.x', '16.11.x', '17.x'] include: - os: windows-latest - node-version: '16.x' + node-version: '17.x' - os: macos-latest - node-version: '16.x' + node-version: '17.x' steps: - name: set git core.autocrlf to 'input' diff --git a/javascript/src/Argument.ts b/javascript/src/Argument.ts index 56d8c454a..52978d063 100644 --- a/javascript/src/Argument.ts +++ b/javascript/src/Argument.ts @@ -11,8 +11,8 @@ export default class Argument { if (argGroups.length !== parameterTypes.length) { throw new CucumberExpressionError( - `Group has ${argGroups.length} capture groups (${argGroups.map( - (g) => g.value + `Group has ${argGroups.length} capture groups (${argGroups.map((g) => + g ? g.value : null )}), but there were ${parameterTypes.length} parameter types (${parameterTypes.map( (p) => p.name )})` @@ -22,7 +22,10 @@ export default class Argument { return parameterTypes.map((parameterType, i) => new Argument(argGroups[i], parameterType)) } - constructor(public readonly group: Group, public readonly parameterType: ParameterType) { + constructor( + public readonly group: Group | null, + public readonly parameterType: ParameterType + ) { this.group = group this.parameterType = parameterType } @@ -33,7 +36,7 @@ export default class Argument { * @param thisObj the object in which the transformer function is applied. */ public getValue(thisObj: unknown): T | null { - const groupValues = this.group ? this.group.values : null + const groupValues = this.group ? this.group.values : [] return this.parameterType.transform(thisObj, groupValues) } diff --git a/javascript/src/Group.ts b/javascript/src/Group.ts index 60535da5b..8aebce730 100644 --- a/javascript/src/Group.ts +++ b/javascript/src/Group.ts @@ -1,12 +1,12 @@ export default class Group { constructor( public readonly value: string, - public readonly start: number | undefined, - public readonly end: number | undefined, - public readonly children: readonly Group[] + public readonly start: number, + public readonly end: number, + public readonly children: readonly (Group | null)[] ) {} - get values(): string[] | null { - return (this.children.length === 0 ? [this] : this.children).map((g) => g.value) + get values(): (string | null)[] { + return (this.children.length === 0 ? [this] : this.children).map((g) => (g ? g.value : null)) } } diff --git a/javascript/src/GroupBuilder.ts b/javascript/src/GroupBuilder.ts index 3ecbeb234..629f07832 100644 --- a/javascript/src/GroupBuilder.ts +++ b/javascript/src/GroupBuilder.ts @@ -11,13 +11,14 @@ export default class GroupBuilder { this.groupBuilders.push(groupBuilder) } - public build(match: RegExpExecArray, nextGroupIndex: () => number): Group { + public build(match: RegExpExecArray, nextGroupIndex: () => number): Group | null { const groupIndex = nextGroupIndex() const children = this.groupBuilders.map((gb) => gb.build(match, nextGroupIndex)) const value = match[groupIndex] + if (value === undefined) return null const index = match.indices[groupIndex] - const start = index ? index[0] : undefined - const end = index ? index[1] : undefined + const start = index[0] + const end = index[1] return new Group(value, start, end, children) } diff --git a/javascript/src/ParameterType.ts b/javascript/src/ParameterType.ts index 3dc83f65b..33b71a4c3 100644 --- a/javascript/src/ParameterType.ts +++ b/javascript/src/ParameterType.ts @@ -65,7 +65,7 @@ export default class ParameterType { this.transformFn = transform } - public transform(thisObj: unknown, groupValues: string[] | null) { + public transform(thisObj: unknown, groupValues: (string | null)[]) { return this.transformFn.apply(thisObj, groupValues) } } diff --git a/javascript/test/CucumberExpressionTest.ts b/javascript/test/CucumberExpressionTest.ts index 86c4419d1..95c8777f6 100644 --- a/javascript/test/CucumberExpressionTest.ts +++ b/javascript/test/CucumberExpressionTest.ts @@ -85,7 +85,7 @@ describe('CucumberExpression', () => { assert.strictEqual(new CucumberExpression(expr, new ParameterTypeRegistry()).source, expr) }) - it('unmatched optional groups have undefined values', () => { + it('unmatched optional groups have null values', () => { const parameterTypeRegistry = new ParameterTypeRegistry() parameterTypeRegistry.defineParameterType( new ParameterType( @@ -103,8 +103,8 @@ describe('CucumberExpression', () => { const world = {} - assert.deepStrictEqual(expression.match(`TLA`)![0].getValue(world), ['TLA', undefined]) - assert.deepStrictEqual(expression.match(`123`)![0].getValue(world), [undefined, '123']) + assert.deepStrictEqual(expression.match(`TLA`)![0].getValue(world), ['TLA', null]) + assert.deepStrictEqual(expression.match(`123`)![0].getValue(world), [null, '123']) }) // JavaScript-specific diff --git a/javascript/test/RegularExpressionTest.ts b/javascript/test/RegularExpressionTest.ts index 641433edd..8594a011e 100644 --- a/javascript/test/RegularExpressionTest.ts +++ b/javascript/test/RegularExpressionTest.ts @@ -14,6 +14,17 @@ interface Expectation { } describe('RegularExpression', () => { +<<<<<<< HEAD + it('documents match arguments', () => { + const parameterRegistry = new ParameterTypeRegistry() + + const expr = /I have (\d+) cukes? in my (\w+) now/ + const expression = new RegularExpression(expr, parameterRegistry) + const args = expression.match('I have 7 cukes in my belly now')! + assert.strictEqual(7, args[0].getValue(null)) + assert.strictEqual('belly', args[1].getValue(null)) + }) +======= for (const path of glob.sync(`${testDataDir}/regular-expression/matching/*.yaml`)) { const expectation = yaml.load(fs.readFileSync(path, 'utf-8')) as Expectation it(`matches ${path}`, () => { @@ -29,6 +40,7 @@ describe('RegularExpression', () => { ) }) } +>>>>>>> main it('does no transform by default', () => { assert.deepStrictEqual(match(/(\d\d)/, '22'), ['22']) diff --git a/javascript/test/TreeRegexpTest.ts b/javascript/test/TreeRegexpTest.ts index f933b1f0f..1e5467f85 100644 --- a/javascript/test/TreeRegexpTest.ts +++ b/javascript/test/TreeRegexpTest.ts @@ -15,9 +15,9 @@ describe('TreeRegexp', () => { const tr = new TreeRegexp(/(a(?:b)?)(c)/) const group = tr.match('ac')! assert.strictEqual(group.value, 'ac') - assert.strictEqual(group.children[0].value, 'a') - assert.deepStrictEqual(group.children[0].children, []) - assert.strictEqual(group.children[1].value, 'c') + assert.strictEqual(group.children[0]!.value, 'a') + assert.deepStrictEqual(group.children[0]!.children, []) + assert.strictEqual(group.children[1]!.value, 'c') }) it('ignores `?:` as a non-capturing group', () => { @@ -39,7 +39,7 @@ describe('TreeRegexp', () => { const group = tr.match('abc')! assert.strictEqual(group.value, 'abc') assert.strictEqual(group.children.length, 1) - assert.strictEqual(group.children[0].value, 'bc') + assert.strictEqual(group.children[0]!.value, 'bc') }) it('ignores `?<=` as a non-capturing group', () => { @@ -47,7 +47,7 @@ describe('TreeRegexp', () => { const group = tr.match('abc')! assert.strictEqual(group.value, 'abc') assert.strictEqual(group.children.length, 1) - assert.strictEqual(group.children[0].value, 'bc') + assert.strictEqual(group.children[0]!.value, 'bc') }) it('ignores `? { @@ -55,7 +55,7 @@ describe('TreeRegexp', () => { const group = tr.match('abc')! assert.strictEqual(group.value, 'abc') assert.strictEqual(group.children.length, 1) - assert.strictEqual(group.children[0].value, 'bc') + assert.strictEqual(group.children[0]!.value, 'bc') }) it('matches named capturing group', () => { @@ -63,13 +63,13 @@ describe('TreeRegexp', () => { const group = tr.match('abc')! assert.strictEqual(group.value, 'abc') assert.strictEqual(group.children.length, 1) - assert.strictEqual(group.children[0].value, 'b') + assert.strictEqual(group.children[0]!.value, 'b') }) it('matches optional group', () => { const tr = new TreeRegexp(/^Something( with an optional argument)?/) const group = tr.match('Something')! - assert.strictEqual(group.children[0].value, undefined) + assert.strictEqual(group.children[0], null) }) it('matches nested groups', () => { @@ -78,15 +78,15 @@ describe('TreeRegexp', () => { ) const group = tr.match('A 5 thick line from 10,20,30 to 40,50,60')! - assert.strictEqual(group.children[0].value, '5') - assert.strictEqual(group.children[1].value, '10,20,30') - assert.strictEqual(group.children[1].children[0].value, '10') - assert.strictEqual(group.children[1].children[1].value, '20') - assert.strictEqual(group.children[1].children[2].value, '30') - assert.strictEqual(group.children[2].value, '40,50,60') - assert.strictEqual(group.children[2].children[0].value, '40') - assert.strictEqual(group.children[2].children[1].value, '50') - assert.strictEqual(group.children[2].children[2].value, '60') + assert.strictEqual(group.children[0]!.value, '5') + assert.strictEqual(group.children[1]!.value, '10,20,30') + assert.strictEqual(group.children[1]!.children[0]!.value, '10') + assert.strictEqual(group.children[1]!.children[1]!.value, '20') + assert.strictEqual(group.children[1]!.children[2]!.value, '30') + assert.strictEqual(group.children[2]!.value, '40,50,60') + assert.strictEqual(group.children[2]!.children[0]!.value, '40') + assert.strictEqual(group.children[2]!.children[1]!.value, '50') + assert.strictEqual(group.children[2]!.children[2]!.value, '60') }) it('detects multiple non capturing groups', () => { @@ -117,7 +117,7 @@ describe('TreeRegexp', () => { const tr = new TreeRegexp('the stdout(?: from "(.*?)")?') const group = tr.match('the stdout')! assert.strictEqual(group.value, 'the stdout') - assert.strictEqual(group.children[0].value, undefined) + assert.strictEqual(group.children[0], null) assert.strictEqual(group.children.length, 1) }) @@ -146,6 +146,6 @@ describe('TreeRegexp', () => { const group = tr.match('drawings: ONE(TWO)')! assert.strictEqual(group.value, 'drawings: ONE(TWO)') assert.strictEqual(group.children.length, 1) - assert.strictEqual(group.children[0].value, 'ONE(TWO)') + assert.strictEqual(group.children[0]!.value, 'ONE(TWO)') }) })