mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
finished reorganizing test suite
This commit is contained in:
parent
ccae9ea6a8
commit
6421c865f5
18 changed files with 442 additions and 416 deletions
3
Cakefile
3
Cakefile
|
@ -165,7 +165,8 @@ runTests = (CoffeeScript) ->
|
||||||
# Our test helper function for delimiting different test cases.
|
# Our test helper function for delimiting different test cases.
|
||||||
global.test = (description, fn) ->
|
global.test = (description, fn) ->
|
||||||
try
|
try
|
||||||
fn()
|
fn.test = {description, currentFile}
|
||||||
|
fn.call(fn)
|
||||||
catch e
|
catch e
|
||||||
e.description = description if description?
|
e.description = description if description?
|
||||||
e.source = fn.toString() if fn.toString?
|
e.source = fn.toString() if fn.toString?
|
||||||
|
|
|
@ -1,165 +0,0 @@
|
||||||
ok(if mySpecialVariable? then false else true)
|
|
||||||
|
|
||||||
mySpecialVariable = false
|
|
||||||
|
|
||||||
ok(if mySpecialVariable? then true else false)
|
|
||||||
|
|
||||||
|
|
||||||
# Existential assignment.
|
|
||||||
a = 5
|
|
||||||
a = null
|
|
||||||
a ?= 10
|
|
||||||
b ?= 10
|
|
||||||
|
|
||||||
ok a is 10 and b is 10
|
|
||||||
|
|
||||||
|
|
||||||
# The existential operator.
|
|
||||||
z = null
|
|
||||||
x = z ? "EX"
|
|
||||||
ok z is null and x is "EX"
|
|
||||||
|
|
||||||
i = 9
|
|
||||||
func = -> i += 1
|
|
||||||
result = func() ? 101
|
|
||||||
ok result is 10
|
|
||||||
|
|
||||||
# Only evaluate once.
|
|
||||||
counter = 0
|
|
||||||
getNextNode = ->
|
|
||||||
throw "up" if counter
|
|
||||||
counter++
|
|
||||||
|
|
||||||
ok(if getNextNode()? then true else false)
|
|
||||||
|
|
||||||
|
|
||||||
# Existence chains, soaking up undefined properties:
|
|
||||||
obj =
|
|
||||||
prop: "hello"
|
|
||||||
|
|
||||||
eq obj?.prop, "hello"
|
|
||||||
eq obj?['prop'], "hello"
|
|
||||||
eq obj.prop?.length, 5
|
|
||||||
eq obj?.prop?['length'], 5
|
|
||||||
eq obj?.prop?.non?.existent?.property, undefined
|
|
||||||
|
|
||||||
|
|
||||||
# Soaks and caches method calls as well.
|
|
||||||
arr = ["--", "----"]
|
|
||||||
|
|
||||||
eq arr.pop()?.length, 4
|
|
||||||
eq arr.pop()?.length, 2
|
|
||||||
eq arr.pop()?.length, undefined
|
|
||||||
eq arr.pop()?.length?.non?.existent()?.property, undefined
|
|
||||||
|
|
||||||
|
|
||||||
# Soaks method calls safely.
|
|
||||||
value = null
|
|
||||||
eq value?.toString().toLowerCase(), undefined
|
|
||||||
|
|
||||||
value = 10
|
|
||||||
eq value?.toString().toLowerCase(), '10'
|
|
||||||
|
|
||||||
eq 0.nothing?.property() or 101, 101
|
|
||||||
|
|
||||||
counter = 0
|
|
||||||
func = ->
|
|
||||||
counter += 1
|
|
||||||
'prop'
|
|
||||||
obj =
|
|
||||||
prop: -> this
|
|
||||||
value: 25
|
|
||||||
|
|
||||||
ok obj[func()]()[func()]()[func()]()?.value is 25
|
|
||||||
ok counter is 3
|
|
||||||
|
|
||||||
|
|
||||||
ident = (obj) -> obj
|
|
||||||
eq ident(non?.existent().method()), undefined, 'soaks inner values'
|
|
||||||
|
|
||||||
|
|
||||||
# Soaks constructor invocations.
|
|
||||||
a = 0
|
|
||||||
class Foo
|
|
||||||
constructor: -> a += 1
|
|
||||||
bar: "bat"
|
|
||||||
|
|
||||||
ok (new Foo())?.bar is 'bat'
|
|
||||||
ok a is 1
|
|
||||||
|
|
||||||
|
|
||||||
ok not value?.property?, 'safely checks existence on soaks'
|
|
||||||
|
|
||||||
|
|
||||||
eq nothing?.value, undefined, 'safely calls values off of non-existent variables'
|
|
||||||
eq !nothing?.value and 1, 1, 'corresponding operators work as expected'
|
|
||||||
|
|
||||||
|
|
||||||
# Assign to the result of an exsitential operation with a minus.
|
|
||||||
x = null ? - 1
|
|
||||||
ok x is - 1
|
|
||||||
|
|
||||||
|
|
||||||
# Things that compile to ternaries should force parentheses, like operators do.
|
|
||||||
duration = if options?.animated then 150 else 0
|
|
||||||
ok duration is 0
|
|
||||||
|
|
||||||
|
|
||||||
# Function soaks.
|
|
||||||
plus1 = (x) -> x + 1
|
|
||||||
count = 0
|
|
||||||
obj = {
|
|
||||||
counter: -> count += 1; this
|
|
||||||
returnThis: -> this
|
|
||||||
}
|
|
||||||
|
|
||||||
eq plus1?(41), 42
|
|
||||||
eq (plus1? 41), 42
|
|
||||||
eq plus2?(41), undefined
|
|
||||||
eq (plus2? 41), undefined
|
|
||||||
eq obj.returnThis?(), obj
|
|
||||||
eq obj.returnSelf?(), undefined
|
|
||||||
eq obj.returnThis?().flag = on, on
|
|
||||||
eq obj.returnSelf?().flag = on, undefined
|
|
||||||
eq obj.counter().counter().returnThis?(), obj
|
|
||||||
eq count, 2
|
|
||||||
|
|
||||||
maybe_close = (f, arg) -> if typeof f is 'function' then () -> f(arg) else -1
|
|
||||||
|
|
||||||
eq maybe_close(plus1, 41)?(), 42
|
|
||||||
eq (maybe_close plus1, 41)?(), 42
|
|
||||||
eq (maybe_close 'string', 41)?(), undefined
|
|
||||||
|
|
||||||
eq 2?(3), undefined
|
|
||||||
eq new Number?(42) | 0, 42
|
|
||||||
eq new Bumper?(42) | 0, 0
|
|
||||||
|
|
||||||
|
|
||||||
#726
|
|
||||||
eq calendar?[Date()], undefined
|
|
||||||
|
|
||||||
|
|
||||||
#733
|
|
||||||
a = b: {c: null}
|
|
||||||
eq a.b?.c?(), undefined
|
|
||||||
|
|
||||||
a.b?.c or= (it) -> it
|
|
||||||
eq a.b?.c?(1), 1
|
|
||||||
eq a.b?.c?([2, 3]...), 2
|
|
||||||
|
|
||||||
|
|
||||||
#756
|
|
||||||
a = null
|
|
||||||
ok isNaN a?.b.c + 1
|
|
||||||
eq undefined, a?.b.c += 1
|
|
||||||
eq undefined, ++a?.b.c
|
|
||||||
eq undefined, delete a?.b.c
|
|
||||||
|
|
||||||
a = b: {c: 0}
|
|
||||||
eq 1, a?.b.c + 1
|
|
||||||
eq 1, a?.b.c += 1
|
|
||||||
eq 2, ++a?.b.c
|
|
||||||
eq yes, delete a?.b.c
|
|
||||||
|
|
||||||
|
|
||||||
eq (1 or 0)?, true, 'postfix `?` should unwrap correctly'
|
|
|
@ -1,162 +0,0 @@
|
||||||
# Simple variable swapping.
|
|
||||||
a = -1
|
|
||||||
b = -2
|
|
||||||
|
|
||||||
[a, b] = [b, a]
|
|
||||||
|
|
||||||
eq a, -2
|
|
||||||
eq b, -1
|
|
||||||
|
|
||||||
func = ->
|
|
||||||
[a, b] = [b, a]
|
|
||||||
|
|
||||||
eq func().join(' '), '-1 -2'
|
|
||||||
eq a, -1
|
|
||||||
eq b, -2
|
|
||||||
|
|
||||||
#713
|
|
||||||
eq (onetwo = [1, 2]), [a, b] = [c, d] = onetwo
|
|
||||||
ok a is c is 1 and b is d is 2
|
|
||||||
|
|
||||||
|
|
||||||
# Array destructuring, including splats.
|
|
||||||
[x,y...,z] = [1,2,3,4,5]
|
|
||||||
|
|
||||||
ok x is 1
|
|
||||||
ok y.length is 3
|
|
||||||
ok z is 5
|
|
||||||
|
|
||||||
[x, [y, mids..., last], z..., end] = [1, [10, 20, 30, 40], 2,3,4, 5]
|
|
||||||
|
|
||||||
ok x is 1
|
|
||||||
ok y is 10
|
|
||||||
ok mids.length is 2 and mids[1] is 30
|
|
||||||
ok last is 40
|
|
||||||
ok z.length is 3 and z[2] is 4
|
|
||||||
ok end is 5
|
|
||||||
|
|
||||||
|
|
||||||
# Object destructuring.
|
|
||||||
obj = {x: 10, y: 20, z: 30}
|
|
||||||
|
|
||||||
{x: a, y: b, z: c} = obj
|
|
||||||
|
|
||||||
ok a is 10
|
|
||||||
ok b is 20
|
|
||||||
ok c is 30
|
|
||||||
|
|
||||||
person = {
|
|
||||||
name: "Moe"
|
|
||||||
family: {
|
|
||||||
'elder-brother': {
|
|
||||||
addresses: [
|
|
||||||
"first"
|
|
||||||
{
|
|
||||||
street: "101 Deercreek Ln."
|
|
||||||
city: "Moquasset NY, 10021"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{name: a, family: {'elder-brother': {addresses: [one, {city: b}]}}} = person
|
|
||||||
|
|
||||||
ok a is "Moe"
|
|
||||||
ok b is "Moquasset NY, 10021"
|
|
||||||
|
|
||||||
test = {
|
|
||||||
person: {
|
|
||||||
address: [
|
|
||||||
"------"
|
|
||||||
"Street 101"
|
|
||||||
"Apt 101"
|
|
||||||
"City 101"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{person: {address: [ignore, addr...]}} = test
|
|
||||||
|
|
||||||
ok addr.join(', ') is "Street 101, Apt 101, City 101"
|
|
||||||
|
|
||||||
|
|
||||||
# Pattern matching against an expression.
|
|
||||||
[a, b] = if true then [2, 1] else [1, 2]
|
|
||||||
|
|
||||||
ok a is 2
|
|
||||||
ok b is 1
|
|
||||||
|
|
||||||
|
|
||||||
# Pattern matching with object shorthand.
|
|
||||||
|
|
||||||
person = {
|
|
||||||
name: "Bob"
|
|
||||||
age: 26
|
|
||||||
dogs: ["Prince", "Bowie"]
|
|
||||||
}
|
|
||||||
|
|
||||||
{name, age, dogs: [first, second]} = person
|
|
||||||
|
|
||||||
ok name is "Bob"
|
|
||||||
ok age is 26
|
|
||||||
ok first is "Prince"
|
|
||||||
ok second is "Bowie"
|
|
||||||
|
|
||||||
# Pattern matching within for..loops.
|
|
||||||
|
|
||||||
persons = {
|
|
||||||
George: { name: "Bob" },
|
|
||||||
Bob: { name: "Alice" }
|
|
||||||
Christopher: { name: "Stan" }
|
|
||||||
}
|
|
||||||
|
|
||||||
join1 = ("#{key}: #{name}" for key, { name } of persons)
|
|
||||||
|
|
||||||
eq join1.join(' / '), "George: Bob / Bob: Alice / Christopher: Stan"
|
|
||||||
|
|
||||||
persons = [
|
|
||||||
{ name: "Bob", parent: { name: "George" } },
|
|
||||||
{ name: "Alice", parent: { name: "Bob" } },
|
|
||||||
{ name: "Stan", parent: { name: "Christopher" } }
|
|
||||||
]
|
|
||||||
|
|
||||||
join2 = ("#{parent}: #{name}" for { name, parent: { name: parent } } in persons)
|
|
||||||
|
|
||||||
eq join1.join(' '), join2.join(' ')
|
|
||||||
|
|
||||||
persons = [['Bob', ['George']], ['Alice', ['Bob']], ['Stan', ['Christopher']]]
|
|
||||||
join3 = ("#{parent}: #{name}" for [name, [parent]] in persons)
|
|
||||||
|
|
||||||
eq join2.join(' '), join3.join(' ')
|
|
||||||
|
|
||||||
|
|
||||||
# Pattern matching doesn't clash with implicit block objects.
|
|
||||||
obj = a: 101
|
|
||||||
func = -> true
|
|
||||||
|
|
||||||
if func func
|
|
||||||
{a} = obj
|
|
||||||
|
|
||||||
ok a is 101
|
|
||||||
|
|
||||||
[x] = {0: y} = {'0': z} = [Math.random()]
|
|
||||||
ok x is y is z, 'destructuring in multiple'
|
|
||||||
|
|
||||||
|
|
||||||
# Destructuring into an object.
|
|
||||||
obj =
|
|
||||||
func: (list, object) ->
|
|
||||||
[@one, @two] = list
|
|
||||||
{@a, @b} = object
|
|
||||||
{@a} = object
|
|
||||||
null
|
|
||||||
|
|
||||||
{} = [] = ok yes, 'empty assignment is allowed'
|
|
||||||
|
|
||||||
obj.func [1, 2], a: 'a', b: 'b'
|
|
||||||
|
|
||||||
eq obj.one, 1
|
|
||||||
eq obj.two, 2
|
|
||||||
eq obj.a, 'a'
|
|
||||||
eq obj.b, 'b'
|
|
|
@ -5,6 +5,7 @@
|
||||||
# * Splats in Array Literals
|
# * Splats in Array Literals
|
||||||
|
|
||||||
# TODO: refactor array literal tests
|
# TODO: refactor array literal tests
|
||||||
|
# TODO: add indexing and method invocation tests: [1][0] is 1, [].toString()
|
||||||
|
|
||||||
trailingComma = [1, 2, 3,]
|
trailingComma = [1, 2, 3,]
|
||||||
ok (trailingComma[0] is 1) and (trailingComma[2] is 3) and (trailingComma.length is 3)
|
ok (trailingComma[0] is 1) and (trailingComma[2] is 3) and (trailingComma.length is 3)
|
||||||
|
|
|
@ -29,6 +29,79 @@ test "compound assignments should not declare", ->
|
||||||
|
|
||||||
#### Compound Assignment
|
#### Compound Assignment
|
||||||
|
|
||||||
|
test "boolean operators", ->
|
||||||
|
nonce = {}
|
||||||
|
|
||||||
|
a = 0
|
||||||
|
a or= nonce
|
||||||
|
eq nonce, a
|
||||||
|
|
||||||
|
b = 1
|
||||||
|
b or= nonce
|
||||||
|
eq 1, b
|
||||||
|
|
||||||
|
c = 0
|
||||||
|
c and= nonce
|
||||||
|
eq 0, c
|
||||||
|
|
||||||
|
d = 1
|
||||||
|
d and= nonce
|
||||||
|
eq nonce, d
|
||||||
|
|
||||||
|
# ensure that RHS is treated as a group
|
||||||
|
e = f = false
|
||||||
|
e and= f or true
|
||||||
|
eq false, e
|
||||||
|
|
||||||
|
test "compound assignment as a sub expression", ->
|
||||||
|
[a, b, c] = [1, 2, 3]
|
||||||
|
eq 6, (a + b += c)
|
||||||
|
eq 1, a
|
||||||
|
eq 5, b
|
||||||
|
eq 3, c
|
||||||
|
|
||||||
|
# *note: this test could still use refactoring*
|
||||||
|
test "compound assignment should be careful about caching variables", ->
|
||||||
|
count = 0
|
||||||
|
list = []
|
||||||
|
|
||||||
|
list[++count] or= 1
|
||||||
|
eq 1, list[1]
|
||||||
|
eq 1, count
|
||||||
|
|
||||||
|
list[++count] ?= 2
|
||||||
|
eq 2, list[2]
|
||||||
|
eq 2, count
|
||||||
|
|
||||||
|
list[count++] and= 6
|
||||||
|
eq 6, list[2]
|
||||||
|
eq 3, count
|
||||||
|
|
||||||
|
base = ->
|
||||||
|
++count
|
||||||
|
base
|
||||||
|
|
||||||
|
base().four or= 4
|
||||||
|
eq 4, base.four
|
||||||
|
eq 4, count
|
||||||
|
|
||||||
|
base().five ?= 5
|
||||||
|
eq 5, base.five
|
||||||
|
eq 5, count
|
||||||
|
|
||||||
|
test "compound assignment with implicit objects", ->
|
||||||
|
obj = undefined
|
||||||
|
obj ?=
|
||||||
|
one: 1
|
||||||
|
|
||||||
|
eq 1, obj.one
|
||||||
|
|
||||||
|
obj and=
|
||||||
|
two: 2
|
||||||
|
|
||||||
|
eq undefined, obj.one
|
||||||
|
eq 2, obj.two
|
||||||
|
|
||||||
test "compound assignment (math operators)", ->
|
test "compound assignment (math operators)", ->
|
||||||
num = 10
|
num = 10
|
||||||
num -= 5
|
num -= 5
|
||||||
|
@ -65,5 +138,128 @@ test "more compound assignment", ->
|
||||||
|
|
||||||
#### Destructuring Assignment
|
#### Destructuring Assignment
|
||||||
|
|
||||||
# NO TESTS?!
|
test "empty destructuring assignment", ->
|
||||||
# TODO: make tests for destructuring assignment
|
{} = [] = undefined
|
||||||
|
|
||||||
|
test "chained destructuring assignments", ->
|
||||||
|
[a] = {0: b} = {'0': c} = [nonce={}]
|
||||||
|
eq nonce, a
|
||||||
|
eq nonce, b
|
||||||
|
eq nonce, c
|
||||||
|
|
||||||
|
test "variable swapping to verify caching of RHS values when appropriate", ->
|
||||||
|
a = nonceA = {}
|
||||||
|
b = nonceB = {}
|
||||||
|
c = nonceC = {}
|
||||||
|
[a, b, c] = [b, c, a]
|
||||||
|
eq nonceB, a
|
||||||
|
eq nonceC, b
|
||||||
|
eq nonceA, c
|
||||||
|
[a, b, c] = [b, c, a]
|
||||||
|
eq nonceC, a
|
||||||
|
eq nonceA, b
|
||||||
|
eq nonceB, c
|
||||||
|
fn = ->
|
||||||
|
[a, b, c] = [b, c, a]
|
||||||
|
arrayEq [nonceA,nonceB,nonceC], fn()
|
||||||
|
eq nonceA, a
|
||||||
|
eq nonceB, b
|
||||||
|
eq nonceC, c
|
||||||
|
|
||||||
|
test "#713", ->
|
||||||
|
nonces = [nonceA={},nonceB={}]
|
||||||
|
eq nonces, [a, b] = [c, d] = nonces
|
||||||
|
eq nonceA, a
|
||||||
|
eq nonceA, c
|
||||||
|
eq nonceB, b
|
||||||
|
eq nonceB, d
|
||||||
|
|
||||||
|
test "destructuring assignment with splats", ->
|
||||||
|
a = {}; b = {}; c = {}; d = {}; e = {}
|
||||||
|
[x,y...,z] = [a,b,c,d,e]
|
||||||
|
eq a, x
|
||||||
|
arrayEq [b,c,d], y
|
||||||
|
eq e, z
|
||||||
|
|
||||||
|
test "deep destructuring assignment with splats", ->
|
||||||
|
a={}; b={}; c={}; d={}; e={}; f={}; g={}; h={}; i={}
|
||||||
|
[u, [v, w..., x], y..., z] = [a, [b, c, d, e], f, g, h, i]
|
||||||
|
eq a, u
|
||||||
|
eq b, v
|
||||||
|
arrayEq [c,d], w
|
||||||
|
eq e, x
|
||||||
|
arrayEq [f,g,h], y
|
||||||
|
eq i, z
|
||||||
|
|
||||||
|
test "destructuring assignment with objects", ->
|
||||||
|
a={}; b={}; c={}
|
||||||
|
obj = {a,b,c}
|
||||||
|
{a:x, b:y, c:z} = obj
|
||||||
|
eq a, x
|
||||||
|
eq b, y
|
||||||
|
eq c, z
|
||||||
|
|
||||||
|
test "deep destructuring assignment with objects", ->
|
||||||
|
a={}; b={}; c={}; d={}
|
||||||
|
obj = {
|
||||||
|
a
|
||||||
|
b: {
|
||||||
|
'c': {
|
||||||
|
d: [
|
||||||
|
b
|
||||||
|
{e: c, f: d}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{a: w, 'b': {c: d: [x, {'f': z, e: y}]}} = obj
|
||||||
|
eq a, w
|
||||||
|
eq b, x
|
||||||
|
eq c, y
|
||||||
|
eq d, z
|
||||||
|
|
||||||
|
test "destructuring assignment with objects and splats", ->
|
||||||
|
a={}; b={}; c={}; d={}
|
||||||
|
obj = a: b: [a, b, c, d]
|
||||||
|
{a: b: [y, z...]} = obj
|
||||||
|
eq a, y
|
||||||
|
arrayEq [b,c,d], z
|
||||||
|
|
||||||
|
test "destructuring assignment against an expression", ->
|
||||||
|
a={}; b={}
|
||||||
|
[y, z] = if true then [a, b] else [b, a]
|
||||||
|
eq a, y
|
||||||
|
eq b, z
|
||||||
|
|
||||||
|
# for implicit destructuring assignment in comprehensions, see the comprehension tests
|
||||||
|
|
||||||
|
test "destructuring assignment with context (@) properties", ->
|
||||||
|
a={}; b={}; c={}; d={}; e={}
|
||||||
|
obj =
|
||||||
|
fn: () ->
|
||||||
|
local = [a, {b, c}, d, e]
|
||||||
|
[@a, {b: @b, c: @c}, @d, @e] = local
|
||||||
|
eq undefined, obj[key] for key in ['a','b','c','d','e']
|
||||||
|
obj.fn()
|
||||||
|
eq a, obj.a
|
||||||
|
eq b, obj.b
|
||||||
|
eq c, obj.c
|
||||||
|
eq d, obj.d
|
||||||
|
eq e, obj.e
|
||||||
|
|
||||||
|
|
||||||
|
#### Existential Assignment
|
||||||
|
|
||||||
|
test "existential assignment", ->
|
||||||
|
nonce = {}
|
||||||
|
a = false
|
||||||
|
a ?= nonce
|
||||||
|
eq false, a
|
||||||
|
b = undefined
|
||||||
|
b ?= nonce
|
||||||
|
eq nonce, b
|
||||||
|
c = null
|
||||||
|
c ?= nonce
|
||||||
|
eq nonce, c
|
||||||
|
d ?= nonce
|
||||||
|
eq nonce, d
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
# Boolean Literals
|
||||||
|
# ----------------
|
||||||
|
|
||||||
|
# TODO: add method invocation tests: true.toString() is "true"
|
||||||
|
|
||||||
#764: Boolean should be indexable
|
#764: Boolean should be indexable
|
||||||
eq Boolean::toString, true['toString']
|
eq Boolean::toString, true['toString']
|
||||||
|
|
|
@ -262,7 +262,7 @@ ok c instanceof Function
|
||||||
# Classes with value'd constructors.
|
# Classes with value'd constructors.
|
||||||
counter = 0
|
counter = 0
|
||||||
classMaker = ->
|
classMaker = ->
|
||||||
counter += 1
|
counter++
|
||||||
inner = counter
|
inner = counter
|
||||||
->
|
->
|
||||||
@value = inner
|
@value = inner
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
# * Array Comprehensions
|
# * Array Comprehensions
|
||||||
# * Range Comprehensions
|
# * Range Comprehensions
|
||||||
# * Object Comprehensions
|
# * Object Comprehensions
|
||||||
|
# * Implicit Destructuring Assignment
|
||||||
# * Comprehensions with Nonstandard Step
|
# * Comprehensions with Nonstandard Step
|
||||||
|
|
||||||
# TODO: refactor comprehension tests
|
# TODO: refactor comprehension tests
|
||||||
|
@ -338,3 +339,32 @@ test "expression conversion under explicit returns", ->
|
||||||
fn = ->
|
fn = ->
|
||||||
return [(nonce for x in [1..3])][0]
|
return [(nonce for x in [1..3])][0]
|
||||||
arrayEq [nonce,nonce,nonce], fn()
|
arrayEq [nonce,nonce,nonce], fn()
|
||||||
|
|
||||||
|
|
||||||
|
#### Implicit Destructuring Assignment
|
||||||
|
|
||||||
|
test "implicit destructuring assignment in object of objects", ->
|
||||||
|
a={}; b={}; c={}
|
||||||
|
obj = {
|
||||||
|
a: { d: a },
|
||||||
|
b: { d: b }
|
||||||
|
c: { d: c }
|
||||||
|
}
|
||||||
|
result = ([y,z] for y, { d: z } of obj)
|
||||||
|
arrayEq [['a',a],['b',b],['c',c]], result
|
||||||
|
|
||||||
|
test "implicit destructuring assignment in array of objects", ->
|
||||||
|
a={}; b={}; c={}; d={}; e={}; f={}
|
||||||
|
arr = [
|
||||||
|
{ a: a, b: { c: b } },
|
||||||
|
{ a: c, b: { c: d } },
|
||||||
|
{ a: e, b: { c: f } }
|
||||||
|
]
|
||||||
|
result = ([y,z] for { a: y, b: { c: z } } in arr)
|
||||||
|
arrayEq [[a,b],[c,d],[e,f]], result
|
||||||
|
|
||||||
|
test "implicit destructuring assignment in array of arrays", ->
|
||||||
|
a={}; b={}; c={}; d={}; e={}; f={}
|
||||||
|
arr = [[a, [b]], [c, [d]], [e, [f]]]
|
||||||
|
result = ([y,z] for [y, [z]] in arr)
|
||||||
|
arrayEq [[a,b],[c,d],[e,f]], result
|
||||||
|
|
|
@ -135,7 +135,7 @@ test "passing a conditional value to a function", ->
|
||||||
test "unmatched `then` should catch implicit calls", ->
|
test "unmatched `then` should catch implicit calls", ->
|
||||||
a = 0
|
a = 0
|
||||||
trueFn = -> true
|
trueFn = -> true
|
||||||
if trueFn undefined then a += 1
|
if trueFn undefined then a++
|
||||||
eq 1, a
|
eq 1, a
|
||||||
|
|
||||||
|
|
||||||
|
@ -260,7 +260,7 @@ value = false
|
||||||
i = 0
|
i = 0
|
||||||
results = until value
|
results = until value
|
||||||
value = true if i is 5
|
value = true if i is 5
|
||||||
i += 1
|
i++
|
||||||
ok i is 6
|
ok i is 6
|
||||||
|
|
||||||
# Loop
|
# Loop
|
||||||
|
|
|
@ -234,8 +234,7 @@ val = 5
|
||||||
ok (func --val) is 5
|
ok (func --val) is 5
|
||||||
|
|
||||||
test "#855: execution context for `func arr...` should be `null`", ->
|
test "#855: execution context for `func arr...` should be `null`", ->
|
||||||
global = @
|
contextTest = -> eq @, global
|
||||||
contextTest = -> ok global is @
|
|
||||||
array = []
|
array = []
|
||||||
contextTest array
|
contextTest array
|
||||||
contextTest.apply null, array
|
contextTest.apply null, array
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# Function Literals
|
# Function Literals
|
||||||
# -----------------
|
# -----------------
|
||||||
|
|
||||||
|
# TODO: add indexing and method invocation tests: (->)[0], (->).call()
|
||||||
|
|
||||||
# * Function Definition
|
# * Function Definition
|
||||||
# * Bound Function Definition
|
# * Bound Function Definition
|
||||||
# * Parameter List Features
|
# * Parameter List Features
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
# -------------------
|
# -------------------
|
||||||
|
|
||||||
# TODO: refactor javascript literal tests
|
# TODO: refactor javascript literal tests
|
||||||
|
# TODO: add indexing and method invocation tests: `[1]`[0] is 1, `function(){}`.call()
|
||||||
|
|
||||||
eq '\\`', `
|
eq '\\`', `
|
||||||
// Inline JS
|
// Inline JS
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
# ---------------
|
# ---------------
|
||||||
|
|
||||||
# TODO: refactor object literal tests
|
# TODO: refactor object literal tests
|
||||||
|
# TODO: add indexing and method invocation tests: {a}['a'] is a, {a}.a()
|
||||||
|
|
||||||
trailingComma = {k1: "v1", k2: 4, k3: (-> true),}
|
trailingComma = {k1: "v1", k2: 4, k3: (-> true),}
|
||||||
ok trailingComma.k3() and (trailingComma.k2 is 4) and (trailingComma.k1 is "v1")
|
ok trailingComma.k3() and (trailingComma.k2 is 4) and (trailingComma.k1 is "v1")
|
||||||
|
@ -138,7 +139,7 @@ obj =
|
||||||
ok obj.one is 'one'
|
ok obj.one is 'one'
|
||||||
ok obj.two is 'three'
|
ok obj.two is 'three'
|
||||||
|
|
||||||
test "", ->
|
test "invoking functions with implicit object literals", ->
|
||||||
generateGetter = (prop) -> (obj) -> obj[prop]
|
generateGetter = (prop) -> (obj) -> obj[prop]
|
||||||
getA = generateGetter 'a'
|
getA = generateGetter 'a'
|
||||||
getArgs = -> arguments
|
getArgs = -> arguments
|
||||||
|
@ -146,35 +147,41 @@ test "", ->
|
||||||
|
|
||||||
result = getA
|
result = getA
|
||||||
a: 10
|
a: 10
|
||||||
ok result is 10
|
eq 10, result
|
||||||
|
|
||||||
result = getA
|
result = getA
|
||||||
"a": 20
|
"a": 20
|
||||||
ok result is 20
|
eq 20, result
|
||||||
|
|
||||||
result = getA a,
|
result = getA a,
|
||||||
b:1
|
b:1
|
||||||
ok result is undefined
|
eq undefined, result
|
||||||
|
|
||||||
# TODO: this looks like a failing test case; verify
|
result = getA b:1
|
||||||
#result = getA
|
a:43
|
||||||
# b:1
|
eq 43, result
|
||||||
# a
|
|
||||||
#ok result is 30
|
result = getA b:1,
|
||||||
|
a:62
|
||||||
|
eq undefined, result
|
||||||
|
|
||||||
|
result = getA
|
||||||
|
b:1
|
||||||
|
a
|
||||||
|
eq undefined, result
|
||||||
|
|
||||||
result = getA
|
result = getA
|
||||||
a:
|
a:
|
||||||
b:2
|
b:2
|
||||||
b:1
|
b:1
|
||||||
ok result.b is 2
|
eq 2, result.b
|
||||||
|
|
||||||
# TODO: should this test be changed? this is unexpected (and not the displayed) behaviour
|
result = getArgs
|
||||||
#result = getArgs
|
a:1
|
||||||
# a:1
|
b
|
||||||
# b
|
c:1
|
||||||
# c:1
|
ok result.length is 3
|
||||||
#ok result.length is 3
|
ok result[2].c is 1
|
||||||
#ok result[2].c is 1
|
|
||||||
|
|
||||||
test "some weird indentation in YAML-style object literals", ->
|
test "some weird indentation in YAML-style object literals", ->
|
||||||
two = (a, b) -> b
|
two = (a, b) -> b
|
||||||
|
|
|
@ -59,80 +59,59 @@ test "use `::` operator on keywords `this` and `@`", ->
|
||||||
eq nonce, obj.withThis()
|
eq nonce, obj.withThis()
|
||||||
|
|
||||||
|
|
||||||
#### Compound Assignment Operators
|
#### Existential Operator (Binary)
|
||||||
|
|
||||||
test "boolean operators", ->
|
test "binary existential operator", ->
|
||||||
nonce = {}
|
nonce = {}
|
||||||
|
|
||||||
|
b = a ? nonce
|
||||||
|
eq nonce, b
|
||||||
|
|
||||||
|
a = null
|
||||||
|
b = undefined
|
||||||
|
b = a ? nonce
|
||||||
|
eq nonce, b
|
||||||
|
|
||||||
|
a = false
|
||||||
|
b = a ? nonce
|
||||||
|
eq false, b
|
||||||
|
|
||||||
a = 0
|
a = 0
|
||||||
a or= nonce
|
b = a ? nonce
|
||||||
eq nonce, a
|
eq 0, b
|
||||||
|
|
||||||
b = 1
|
test "binary existential operator conditionally evaluates second operand", ->
|
||||||
b or= nonce
|
i = 1
|
||||||
eq 1, b
|
func = -> i -= 1
|
||||||
|
result = func() ? func()
|
||||||
|
eq result, 0
|
||||||
|
|
||||||
c = 0
|
test "binary existential operator with negative number", ->
|
||||||
c and= nonce
|
a = null ? - 1
|
||||||
eq 0, c
|
eq -1, a
|
||||||
|
|
||||||
d = 1
|
|
||||||
d and= nonce
|
|
||||||
eq nonce, d
|
|
||||||
|
|
||||||
# ensure that RHS is treated as a group
|
#### Existential Operator (Unary)
|
||||||
e = f = false
|
|
||||||
e and= f or true
|
|
||||||
eq false, e
|
|
||||||
|
|
||||||
test "compound assignment as a sub expression", ->
|
test "postfix existential operator", ->
|
||||||
[a, b, c] = [1, 2, 3]
|
ok (if nonexistent? then false else true)
|
||||||
eq 6, (a + b += c)
|
defined = true
|
||||||
eq 1, a
|
ok defined?
|
||||||
eq 5, b
|
defined = false
|
||||||
eq 3, c
|
ok defined?
|
||||||
|
|
||||||
# *note: this test could still use refactoring*
|
test "postfix existential operator only evaluates its operand once", ->
|
||||||
test "compound assignment should be careful about caching variables", ->
|
semaphore = 0
|
||||||
count = 0
|
fn = ->
|
||||||
list = []
|
ok false if semaphore
|
||||||
|
++semaphore
|
||||||
|
ok(if fn()? then true else false)
|
||||||
|
|
||||||
list[++count] or= 1
|
test "negated postfix existential operator", ->
|
||||||
eq 1, list[1]
|
ok !nothing?.value
|
||||||
eq 1, count
|
|
||||||
|
|
||||||
list[++count] ?= 2
|
test "postfix existential operator on expressions", ->
|
||||||
eq 2, list[2]
|
eq true, (1 or 0)?, true
|
||||||
eq 2, count
|
|
||||||
|
|
||||||
list[count++] and= 6
|
|
||||||
eq 6, list[2]
|
|
||||||
eq 3, count
|
|
||||||
|
|
||||||
base = ->
|
|
||||||
++count
|
|
||||||
base
|
|
||||||
|
|
||||||
base().four or= 4
|
|
||||||
eq 4, base.four
|
|
||||||
eq 4, count
|
|
||||||
|
|
||||||
base().five ?= 5
|
|
||||||
eq 5, base.five
|
|
||||||
eq 5, count
|
|
||||||
|
|
||||||
test "compound assignment with implicit objects", ->
|
|
||||||
obj = undefined
|
|
||||||
obj ?=
|
|
||||||
one: 1
|
|
||||||
|
|
||||||
eq 1, obj.one
|
|
||||||
|
|
||||||
obj and=
|
|
||||||
two: 2
|
|
||||||
|
|
||||||
eq undefined, obj.one
|
|
||||||
eq 2, obj.two
|
|
||||||
|
|
||||||
|
|
||||||
#### `is`,`isnt`,`==`,`!=`
|
#### `is`,`isnt`,`==`,`!=`
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# Range Literals
|
# Range Literals
|
||||||
# --------------
|
# --------------
|
||||||
|
|
||||||
|
# TODO: add indexing and method invocation tests: [1..4][0] is 1, [0...3].toString()
|
||||||
|
|
||||||
# shared array
|
# shared array
|
||||||
shared = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
shared = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# Regular Expression Literals
|
# Regular Expression Literals
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
|
|
||||||
|
# TODO: add method invocation tests: /regex/.toString()
|
||||||
|
|
||||||
# * Regexen
|
# * Regexen
|
||||||
# * Heregexen
|
# * Heregexen
|
||||||
|
|
||||||
|
|
|
@ -4,3 +4,131 @@
|
||||||
# * Soaked Property Access
|
# * Soaked Property Access
|
||||||
# * Soaked Method Invocation
|
# * Soaked Method Invocation
|
||||||
# * Soaked Function Invocation
|
# * Soaked Function Invocation
|
||||||
|
|
||||||
|
|
||||||
|
#### Soaked Property Access
|
||||||
|
|
||||||
|
test "soaked property access", ->
|
||||||
|
nonce = {}
|
||||||
|
obj = a: b: nonce
|
||||||
|
eq nonce , obj?.a.b
|
||||||
|
eq nonce , obj?['a'].b
|
||||||
|
eq nonce , obj.a?.b
|
||||||
|
eq nonce , obj?.a?['b']
|
||||||
|
eq undefined, obj?.a?.non?.existent?.property
|
||||||
|
|
||||||
|
test "soaked property access caches method calls", ->
|
||||||
|
nonce ={}
|
||||||
|
obj = fn: -> a: nonce
|
||||||
|
eq nonce , obj.fn()?.a
|
||||||
|
eq undefined, obj.fn()?.b
|
||||||
|
|
||||||
|
test "soaked property access chaching", ->
|
||||||
|
nonce = {}
|
||||||
|
counter = 0
|
||||||
|
fn = ->
|
||||||
|
counter++
|
||||||
|
'self'
|
||||||
|
obj =
|
||||||
|
self: -> @
|
||||||
|
prop: nonce
|
||||||
|
eq nonce, obj[fn()]()[fn()]()[fn()]()?.prop
|
||||||
|
eq 3, counter
|
||||||
|
|
||||||
|
test "method calls on soaked methods", ->
|
||||||
|
nonce = {}
|
||||||
|
obj = null
|
||||||
|
eq undefined, obj?.a().b()
|
||||||
|
obj = a: -> b: -> nonce
|
||||||
|
eq nonce , obj?.a().b()
|
||||||
|
|
||||||
|
test "postfix existential operator mixes well with soaked property accesses", ->
|
||||||
|
eq false, nonexistent?.property?
|
||||||
|
|
||||||
|
test "function invocation with soaked property access", ->
|
||||||
|
id = (_) -> _
|
||||||
|
eq undefined, id nonexistent?.method()
|
||||||
|
|
||||||
|
test "if-to-ternary should safely parenthesize soaked property accesses", ->
|
||||||
|
ok (if nonexistent?.property then false else true)
|
||||||
|
|
||||||
|
test "#726", ->
|
||||||
|
# TODO: check this test, looks like it's not really testing anything
|
||||||
|
eq undefined, nonexistent?[Date()]
|
||||||
|
|
||||||
|
test "#756", ->
|
||||||
|
# TODO: improve this test
|
||||||
|
a = null
|
||||||
|
ok isNaN a?.b.c + 1
|
||||||
|
eq undefined, a?.b.c += 1
|
||||||
|
eq undefined, ++a?.b.c
|
||||||
|
eq undefined, delete a?.b.c
|
||||||
|
|
||||||
|
test "operations on soaked properties", ->
|
||||||
|
# TODO: improve this test
|
||||||
|
a = b: {c: 0}
|
||||||
|
eq 1, a?.b.c + 1
|
||||||
|
eq 1, a?.b.c += 1
|
||||||
|
eq 2, ++a?.b.c
|
||||||
|
eq yes, delete a?.b.c
|
||||||
|
|
||||||
|
|
||||||
|
#### Soaked Method Invocation
|
||||||
|
|
||||||
|
test "soaked method invocation", ->
|
||||||
|
nonce = {}
|
||||||
|
counter = 0
|
||||||
|
obj =
|
||||||
|
self: -> @
|
||||||
|
increment: -> counter++; @
|
||||||
|
eq obj , obj.self?()
|
||||||
|
eq undefined, obj.method?()
|
||||||
|
eq nonce , obj.self?().property = nonce
|
||||||
|
eq undefined, obj.method?().property = nonce
|
||||||
|
eq obj , obj.increment().increment().self?()
|
||||||
|
eq 2 , counter
|
||||||
|
|
||||||
|
test "#733", ->
|
||||||
|
a = b: {c: null}
|
||||||
|
eq a.b?.c?(), undefined
|
||||||
|
a.b?.c or= (it) -> it
|
||||||
|
eq a.b?.c?(1), 1
|
||||||
|
eq a.b?.c?([2, 3]...), 2
|
||||||
|
|
||||||
|
|
||||||
|
#### Soaked Function Invocation
|
||||||
|
|
||||||
|
test "soaked function invocation", ->
|
||||||
|
nonce = {}
|
||||||
|
id = (_) -> _
|
||||||
|
eq nonce , id?(nonce)
|
||||||
|
eq nonce , (id? nonce)
|
||||||
|
eq undefined, nonexistent?(nonce)
|
||||||
|
eq undefined, (nonexistent? nonce)
|
||||||
|
|
||||||
|
test "soaked function invocation with generated functions", ->
|
||||||
|
nonce = {}
|
||||||
|
id = (_) -> _
|
||||||
|
maybe = (fn, arg) -> if typeof fn is 'function' then () -> fn(arg)
|
||||||
|
eq maybe(id, nonce)?(), nonce
|
||||||
|
eq (maybe id, nonce)?(), nonce
|
||||||
|
eq (maybe false, nonce)?(), undefined
|
||||||
|
|
||||||
|
test "soaked constructor invocation", ->
|
||||||
|
eq 42 , +new Number? 42
|
||||||
|
eq undefined, new Other? 42
|
||||||
|
|
||||||
|
test "soaked constructor invocations with caching and property access", ->
|
||||||
|
semaphore = 0
|
||||||
|
nonce = {}
|
||||||
|
class C
|
||||||
|
constructor: ->
|
||||||
|
ok false if semaphore
|
||||||
|
semaphore++
|
||||||
|
prop: nonce
|
||||||
|
eq nonce, (new C())?.prop
|
||||||
|
eq 1, semaphore
|
||||||
|
|
||||||
|
test "soaked function invocation safe on non-functions", ->
|
||||||
|
eq undefined, 0?(1)
|
||||||
|
eq undefined, 0? 1, 2
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
# ---------------
|
# ---------------
|
||||||
|
|
||||||
# TODO: refactor string literal tests
|
# TODO: refactor string literal tests
|
||||||
|
# TODO: add indexing and method invocation tests: "string"["toString"] is String::toString, "string".toString() is "string"
|
||||||
|
|
||||||
test "backslash escapes", ->
|
test "backslash escapes", ->
|
||||||
eq "\\/\\\\", /\/\\/.source
|
eq "\\/\\\\", /\/\\/.source
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue