Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adding tests for optional chaining proposal #2212

Merged
merged 9 commits into from
Aug 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions features.txt
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ import.meta
WeakRef
FinalizationGroup

# Optional Chaining
# https://github.com/tc39/proposal-optional-chaining
optional-chaining

## Standard language features
#
# Language features that have been included in a published version of the
Expand Down
20 changes: 20 additions & 0 deletions test/language/expressions/optional-chaining/call-expression.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chain on call expression
info: |
Left-Hand-Side Expressions
OptionalExpression:
CallExpression OptionalChain
features: [optional-chaining]
---*/

function fn () {
return {a: 33};
};

// CallExpression Arguments
assert.sameValue(33, fn()?.a);
assert.sameValue(undefined, fn()?.b);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in a follow up PR, we should expand these to other types and checking property existence:

Currently the CallExpression evaluates to:

  1. An Object with the property a

Missing:

  1. The CallExpression evaluates to falsy values
    a. false
    b. 0
    c. ''
    d. undefined // different behavior
    e. null // same as undefined
  2. CallExpression evaluates to other type values.
    a. true
    b. 1
    c. NaN
    d. 'undefined'
    e. Symbol
  3. Verify [[Get]] from the returned object properties

This test itself is good enough as a syntax test, these expansions are more useful for further evaluation of the results.

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
template string passed to tail position of optional chain
info: |
Static Semantics: Early Errors
OptionalChain:
?.TemplateLiteral
OptionalChain TemplateLiteral
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add the error part: It is a Syntax Error if any code matches this production.

features: [optional-chaining]
negative:
type: SyntaxError
phase: parse
---*/

$DONOTEVALUATE();

const a = {fn() {}};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can be a simple var a;. To enable the test, all you need is to have a valid reference for a.


// This production exists in order to prevent automatic semicolon
// insertion rules.
a?.fn
`hello`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a follow up PR: we are missing tests for the ?.TemplateLiteral productions:

a?.`hello`

// and

a?.
  `hello`

Also verify this is valid syntax:

`foo`?.bar

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
template string passed to tail position of optional chain
info: |
Static Semantics: Early Errors
OptionalChain:
?.TemplateLiteral
OptionalChain TemplateLiteral
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add the error part: It is a Syntax Error if any code matches this production.

features: [optional-chaining]
negative:
type: SyntaxError
phase: parse
---*/

$DONOTEVALUATE();

const a = {fn() {}};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name can be simplified here as well.


// This production exists in order to prevent automatic semicolon
// insertion rules.
a?.fn`hello`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chain on member expression
info: |
Left-Hand-Side Expressions
OptionalExpression:
MemberExpression OptionalChain
features: [optional-chaining]
---*/

// PrimaryExpression
// IdentifierReference

const arr = [10, 11];
const fn = (arg1, arg2) => {
return arg1 + arg2;
}
const i = 0;
const obj = {
a: 'hello',
b: {val: 13},
c(arg1) {
return arg1 * 2;
},
arr: [11, 12]
};

// OptionalChain: ?.[Expression]
assert.sameValue(11, arr?.[i + 1]);

// OptionalChain: ?.IdentifierName
assert.sameValue('hello', obj?.a);

// OptionalChain: ?.Arguments
assert.sameValue(30, fn?.(10, 20));

// OptionalChain: OptionalChain [Expression]
assert.sameValue(12, obj?.arr[i + 1]);
assert.throws(TypeError, function() {
obj?.d[i + 1];
});

// OptionalChain: OptionalChain .IdentifierName
assert.sameValue(13, obj?.b.val);
assert.throws(TypeError, function() {
obj?.d.e;
});

// OptionalChain: OptionalChain Arguments
assert.sameValue(20, obj?.c(10));
assert.throws(TypeError, function() {
obj?.d();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chain on recursive optional expression
info: |
Left-Hand-Side Expressions
OptionalExpression:
OptionalExpression OptionalChain
features: [optional-chaining]
---*/

const obj = {
a: {
b: 22
}
};

function fn () {
return {};
}

// MemberExpression
assert.sameValue(22, (obj?.a)?.b);
// CallExpression
assert.sameValue(undefined, (fn()?.a)?.b);
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
ternary operation with decimal does not evaluate as optional chain
info: |
Punctuators
OptionalChainingPunctuator::
?.[lookahead ∉ DecimalDigit]
features: [optional-chaining]
---*/

const value = true ?.30 : false;
assert.sameValue(.30, value);
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
accessing optional value on undefined or null returns undefined.
info: |
If baseValue is undefined or null, then
Return undefined.
features: [optional-chaining]
---*/

const nul = null;
const undf = undefined;
assert.sameValue(undefined, nul?.a);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for a follow up:

var x = 0;
var y = null?.[(function() { x += 1 }())];

// assert value of x to verify if the `[Expression]` has got evaluated
// assert y is undefined.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh! you did that in test/language/expressions/optional-chaining/short-circuiting.js, nice!

assert.sameValue(undefined, undf?.b);
bcoe marked this conversation as resolved.
Show resolved Hide resolved
assert.sameValue(undefined, null?.a);
assert.sameValue(undefined, undefined?.b);
22 changes: 22 additions & 0 deletions test/language/expressions/optional-chaining/short-circuiting.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
demonstrate syntax-based short-circuiting.
info: |
If the expression on the LHS of ?. evaluates to null/undefined, the RHS is
not evaluated
features: [optional-chaining]
---*/

const a = undefined;
let x = 1;

a?.[++x] // short-circuiting.
a?.b.c(++x).d; // long short-circuiting.
bcoe marked this conversation as resolved.
Show resolved Hide resolved

undefined?.[++x] // short-circuiting.
undefined?.b.c(++x).d; // long short-circuiting.

assert.sameValue(1, x);
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
an optional expression cannot be target of assignment
info: |
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression:
OptionalExpression
Return false.
features: [optional-chaining]
negative:
type: SyntaxError
phase: parse
---*/

$DONOTEVALUATE();

const obj = {};

obj?.a = 33;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!