test reorganization waypoint

This commit is contained in:
Michael Ficarra
2010-12-29 00:48:54 -05:00
parent 0fd3ed593c
commit dcfdd144d8
32 changed files with 2160 additions and 2180 deletions

View File

@@ -1,98 +0,0 @@
# Assignment
# ----------
test "context property assignment (using @)", ->
nonce = {}
addMethod = ->
@method = -> nonce
this
eq nonce, addMethod.call({}).method()
test "unassignable values", ->
nonce = {}
for nonref in ['', '""', '0', 'f()'].concat CoffeeScript.RESERVED
eq nonce, (try CoffeeScript.compile "#{nonref} = v" catch e then nonce)
test "compound assignments should not declare", ->
# TODO: make description more clear
# TODO: remove reference to Math
eq Math, (-> Math or= 0)()
#### Statements as Expressions
test "assign the result of a try/catch block", ->
# multiline
result = try
nonexistent * missing
catch error
true
eq true, result
# single line
result = try nonexistent * missing catch error then true
eq true, result
test "conditionals", ->
# assign inside the condition of a conditional statement
nonce = {}
if a = nonce then 1
eq nonce, a
1 if b = nonce
eq nonce, b
# assign the result of a conditional statement
c = if true then nonce
eq nonce, c
test "assign inside the condition of a `while` loop", ->
nonce = {}
count = 1
a = nonce while count--
eq nonce, a
count = 1
while count--
b = nonce
eq nonce, b
#### Compound Assignment
test "compound assignment (math operators)", ->
num = 10
num -= 5
eq 5, num
num *= 10
eq 50, num
num /= 10
eq 5, num
num %= 3
eq 2, num
test "more compound assignment", ->
a = {}
val = undefined
val ||= a
val ||= true
eq a, val
b = {}
val &&= true
eq val, true
val &&= b
eq b, val
c = {}
val = null
val ?= c
val ?= true
eq c, val
#### Destructuring Assignment
# NO TESTS?!
# TODO: make tests for destructuring assignment

View File

@@ -1,18 +0,0 @@
# Break
# -----
test "break at the top level", ->
for i in [1,2,3]
result = i
if i == 2
break
eq 2, result
test "break *not* at the top level", ->
someFunc = () ->
i = 0
while ++i < 3
result = i
break if i > 1
result
eq 2, someFunc()

View File

@@ -1,201 +0,0 @@
# Comments
# --------
# Note: awkward spacing seen in some tests is likely intentional.
test "comments in objects", ->
obj1 = {
# comment
# comment
# comment
one: 1
# comment
two: 2
# comment
}
ok Object::hasOwnProperty.call(obj1,'one')
eq obj1.one, 1
ok Object::hasOwnProperty.call(obj1,'two')
eq obj1.two, 2
test "comments in YAML-style objects", ->
obj2 =
# comment
# comment
# comment
three: 3
# comment
four: 4
# comment
ok Object::hasOwnProperty.call(obj2,'three')
eq obj2.three, 3
ok Object::hasOwnProperty.call(obj2,'four')
eq obj2.four, 4
test "comments following operators that continue lines", ->
sum =
1 +
1 + # comment
1
eq 3, sum
test "comments in functions", ->
fn = ->
# comment
false
false # comment
false
# comment
# comment
true
ok fn()
fn2 = -> #comment
fn()
# comment
ok fn2()
test "trailing comment before an outdent", ->
nonce = {}
fn3 = ->
if true
undefined # comment
nonce
eq nonce, fn3()
test "comments in a switch", ->
nonce = {}
result = switch nonce #comment
# comment
when false then undefined
# comment
when null #comment
undefined
else nonce # comment
eq nonce, result
test "comment with conditional statements", ->
nonce = {}
result = if false # comment
undefined
#comment
else # comment
nonce
# comment
eq nonce, result
test "spaced comments with conditional statements", ->
nonce = {}
result = if false
undefined
# comment
else if false
undefined
# comment
else
nonce
eq nonce, result
#### Block Comments
###
This is a here-comment.
Kind of like a heredoc.
###
test "block comments in objects", ->
a = {}
b = {}
obj = {
a: a
###
comment
###
b: b
}
eq a, obj.a
eq b, obj.b
test "block comments in YAML-style", ->
a = {}
b = {}
obj =
a: a
###
comment
###
b: b
eq a, obj.a
eq b, obj.b
test "block comments in functions", ->
nonce = {}
fn1 = ->
true
###
false
###
ok fn1()
fn2 = ->
###
block comment
###
nonce
eq nonce, fn2()
fn3 = ->
nonce
###
block comment
###
eq nonce, fn3()
fn4 = ->
one = ->
###
block comment
###
two = ->
three = ->
nonce
eq nonce, fn4()()()()
test "block comments inside class bodies", ->
class A
a: ->
###
Comment
###
b: ->
ok A.prototype.b instanceof Function
class B
###
Comment
###
a: ->
b: ->
ok B.prototype.a instanceof Function

View File

@@ -1,181 +0,0 @@
# Conditionals
# ------------
# shared identity function
id = (_) -> if arguments.length is 1 then _ else Array::slice.call(arguments)
#### Basic Conditionals
test "basic conditionals", ->
if false
ok false
else if false
ok false
else
ok true
if true
ok true
else if true
ok false
else
ok true
unless true
ok false
else unless true
ok false
else
ok true
unless false
ok true
else unless false
ok false
else
ok true
test "single-line conditional", ->
if false then ok false else ok true
unless false then ok true else ok false
test "nested conditionals", ->
nonce = {}
eq nonce, (if true
unless false
if false then false else
if true
nonce)
test "nested single-line conditionals", ->
nonce = {}
a = if false then undefined else b = if 0 then undefined else nonce
eq nonce, a
eq nonce, b
c = if false then undefined else (if 0 then undefined else nonce)
eq nonce, c
d = if true then id(if false then undefined else nonce)
eq nonce, d
test "empty conditional bodies", ->
eq undefined, (if false
else if false
else)
test "conditional bodies containing only comments", ->
eq undefined, (if true
###
block comment
###
else
# comment
)
eq undefined, (if false
# comment
else if true
###
block comment
###
else)
test "return value of if-else is from the proper body", ->
nonce = {}
eq nonce, if false then undefined else nonce
test "return value of unless-else is from the proper body", ->
nonce = {}
eq nonce, unless true then undefined else nonce
#### Interactions With Functions
test "single-line function definition with single-line conditional", ->
fn = -> if 1 < 0.5 then 1 else -1
ok fn() is -1
test "function resturns conditional value with no `else`", ->
fn = ->
return if false then true
eq undefined, fn()
test "function returns a conditional value", ->
a = {}
fnA = ->
return if false then undefined else a
eq a, fnA()
b = {}
fnB = ->
return unless false then b else undefined
eq b, fnB()
test "passing a conditional value to a function", ->
nonce = {}
eq nonce, id if false then undefined else nonce
test "unmatched `then` should catch implicit calls", ->
a = 0
trueFn = -> true
if trueFn undefined then a += 1
eq 1, a
#### if-to-ternary
test "if-to-ternary with instanceof requires parentheses", ->
nonce = {}
eq nonce, (if {} instanceof Object
nonce
else
undefined)
test "if-to-ternary as part of a larger operation requires parentheses", ->
ok 2, 1 + if false then 0 else 1
#### Odd Formatting
test "if-else indented within an assignment", ->
nonce = {}
result =
if false
undefined
else
nonce
eq nonce, result
test "suppressed indentation via assignment", ->
nonce = {}
result =
if false then undefined
else if no then undefined
else if 0 then undefined
else if 1 < 0 then undefined
else id(
if false then undefined
else nonce
)
eq nonce, result
test "tight formatting with leading `then`", ->
nonce = {}
eq nonce,
if true
then nonce
else undefined
test "#738", ->
nonce = {}
fn = if true then -> nonce
eq nonce, fn()
test "#748: trailing reserved identifiers", ->
nonce = {}
obj = delete: true
result = if obj.delete
nonce
eq nonce, result

View File

@@ -1,90 +0,0 @@
# Exceptions
# ----------
# shared nonce
nonce = {}
#### Throw
test "basic exception throwing", ->
throws (-> throw 'error'), 'error'
#### Empty Try/Catch/Finally
test "try can exist alone", ->
try
test "try/catch with empty try, empty catch", ->
try
# nothing
catch err
# nothing
test "single-line try/catch with empty try, empty catch", ->
try catch err
test "try/finally with empty try, empty finally", ->
try
# nothing
finally
# nothing
test "single-line try/finally with empty try, empty finally", ->
try finally
test "try/catch/finally with empty try, empty catch, empty finally", ->
try
catch err
finally
test "single-line try/catch/finally with empty try, empty catch, empty finally", ->
try catch err then finally
#### Try/Catch/Finally as an Expression
test "return the result of try when no exception is thrown", ->
result = try
nonce
catch err
undefined
finally
undefined
eq nonce, result
test "single-line result of try when no exception is thrown", ->
result = try nonce catch err then undefined
eq nonce, result
test "return the result of catch when an exception is thrown", ->
fn = ->
try
throw ->
catch err
nonce
doesNotThrow fn
eq nonce, fn()
test "single-line result of catch when an exception is thrown", ->
fn = ->
try throw (->) catch err then nonce
doesNotThrow fn
eq nonce, fn()
test "optional catch", ->
fn = ->
try throw ->
nonce
doesNotThrow fn
eq nonce, fn()
#### Try/Catch/Finally Interaction With Other Constructs
test "try/catch with empty catch as last statement in a function body", ->
fn = ->
try nonce
catch err
eq nonce, fn()

View File

@@ -1,96 +0,0 @@
# Helpers
# -------
# pull the helpers from `CoffeeScript.helpers` into local variables
{starts, ends, compact, count, merge, extend, flatten, del, last} = CoffeeScript.helpers
#### `starts`
test "the `starts` helper tests if a string starts with another string", ->
ok starts('01234', '012')
ok not starts('01234', '123')
test "the `starts` helper can take an optional offset", ->
ok starts('01234', '34', 3)
ok not starts('01234', '01', 1)
#### `ends`
test "the `ends` helper tests if a string ends with another string", ->
ok ends('01234', '234')
ok not ends('01234', '012')
test "the `ends` helper can take an optional offset", ->
ok ends('01234', '012', 2)
ok not ends('01234', '234', 6)
#### `compact`
test "the `compact` helper removes falsey values from an array, preserves truthy ones", ->
allValues = [1, 0, false, obj={}, [], '', ' ', -1, null, undefined, true]
truthyValues = [1, obj, [], ' ', -1, true]
arrayEq truthyValues, compact(allValues)
#### `count`
test "the `count` helper counts the number of occurances of a string in another string", ->
eq 1/0, count('abc', '')
eq 0, count('abc', 'z')
eq 1, count('abc', 'a')
eq 1, count('abc', 'b')
eq 2, count('abcdc', 'c')
eq 2, count('abcdabcd','abc')
#### `merge`
test "the `merge` helper makes a new object with all properties of the objects given as its arguments", ->
ary = [0, 1, 2, 3, 4]
obj = {}
merged = merge obj, ary
ok merged isnt obj
ok merged isnt ary
for own key, val of ary
eq val, merged[key]
#### `extend`
test "the `extend` helper performs a shallow copy", ->
ary = [0, 1, 2, 3]
obj = {}
# should return the object being extended
eq obj, extend(obj, ary)
# should copy the other object's properties as well (obviously)
eq 2, obj[2]
#### `flatten`
test "the `flatten` helper flattens an array", ->
success = yes
(success and= typeof n is 'number') for n in flatten [0, [[[1]], 2], 3, [4]]
ok success
#### `del`
test "the `del` helper deletes a property from an object and returns the deleted value", ->
obj = [0, 1, 2]
eq 1, del(obj, 1)
ok 1 not of obj
#### `last`
test "the `last` helper returns the last item of an array-like object", ->
ary = [0, 1, 2, 3, 4]
eq 4, last(ary)
test "the `last` helper allows one to specify an optional offset", ->
ary = [0, 1, 2, 3, 4]
eq 2, last(ary, 2)

View File

@@ -1,18 +0,0 @@
# Importing
# ---------
unless window? or testingBrowser?
test "coffeescript modules can be imported and executed", ->
magicKey = __filename
magicValue = 0xFFFF
if global[magicKey]?
if exports?
local = magicValue
exports.method = -> local
else
global[magicKey] = {}
if require?.extensions? or require?.registerExtension?
ok require(__filename).method() is magicValue
delete global[magicKey]

View File

@@ -1,225 +0,0 @@
# Operators
# ---------
test "binary (2-ary) math operators do not require spaces", ->
a = 1
b = -1
eq +1, a*-b
eq -1, a*+b
eq +1, a/-b
eq -1, a/+b
test "operators should respect new lines as spaced", ->
a = 123 +
456
eq 579, a
b = "1#{2}3" +
"456"
eq '123456', b
test "multiple operators should space themselves", ->
eq (+ +1), (- -1)
test "bitwise operators", ->
eq 2, (10 & 3)
eq 11, (10 | 3)
eq 9, (10 ^ 3)
eq 80, (10 << 3)
eq 1, (10 >> 3)
eq 1, (10 >>> 3)
num = 10; eq 2, (num &= 3)
num = 10; eq 11, (num |= 3)
num = 10; eq 9, (num ^= 3)
num = 10; eq 80, (num <<= 3)
num = 10; eq 1, (num >>= 3)
num = 10; eq 1, (num >>>= 3)
test "`instanceof`", ->
ok new String instanceof String
ok new Boolean instanceof Boolean
# `instanceof` supports negation by prefixing the operator with `not`
ok new Number not instanceof String
ok new Array not instanceof Boolean
#### Compound Assignment Operators
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
#### `is`,`isnt`,`==`,`!=`
test "`==` and `is` should be interchangeable", ->
a = b = 1
ok a is 1 and b == 1
ok a == b
ok a is b
test "`!=` and `isnt` should be interchangeable", ->
a = 0
b = 1
ok a isnt 1 and b != 0
ok a != b
ok a isnt b
#### `in`, `of`
# - `in` should check if an array contains a value using `indexOf`
# - `of` should check if a property is defined on an object using `in`
test "in, of", ->
arr = [1]
ok 0 of arr
ok 1 in arr
# prefixing `not` to `in and `of` should negate them
ok 1 not of arr
ok 0 not in arr
test "`in` should be able to operate on an array literal", ->
ok 2 in [0, 1, 2, 3]
ok 4 not in [0, 1, 2, 3]
arr = [0, 1, 2, 3]
ok 2 in arr
ok 4 not in arr
# should cache the value used to test the array
arr = [0]
val = 0
ok val++ in arr
ok val++ not in arr
val = 0
ok val++ of arr
ok val++ not of arr
test "`of` and `in` should be able to operate on instance variables", ->
obj = {
list: [2,3]
in_list: (value) -> value in @list
not_in_list: (value) -> value not in @list
of_list: (value) -> value of @list
not_of_list: (value) -> value not of @list
}
ok obj.in_list 3
ok obj.not_in_list 1
ok obj.of_list 0
ok obj.not_of_list 2
test "#???: `in` with cache and `__indexOf` should work in argument lists", ->
eq 1, [Object() in Array()].length
test "#737: `in` should have higher precedence than logical operators", ->
eq 1, 1 in [1] and 1
test "#768: `in` should preserve evaluation order", ->
share = 0
a = -> share++ if share is 0
b = -> share++ if share is 1
c = -> share++ if share is 2
ok a() not in [b(),c()]
eq 3, share
#### Chainable Operators
test "chainable operators", ->
ok 100 > 10 > 1 > 0 > -1
ok -1 < 0 < 1 < 10 < 100
test "`is` and `isnt` may be chained", ->
ok true is not false is true is not false
ok 0 is 0 isnt 1 is 1
test "different comparison operators (`>`,`<`,`is`,etc.) may be combined", ->
ok 1 < 2 > 1
ok 10 < 20 > 2+3 is 5
test "some chainable operators can be negated by `unless`", ->
ok (true unless 0==10!=100)
test "operator precedence: `|` lower than `<`", ->
eq 1, 1 | 2 < 3 < 4
test "preserve references", ->
a = b = c = 1
# `a == b <= c` should become `a === b && b <= c`
# (this test does not seem to test for this)
ok a == b <= c
test "chained operations should evaluate each value only once", ->
a = 0
ok 1 > a++ < 1
test "#891: incorrect inversion of chained comparisons", ->
ok (true unless 0 > 1 > 2)
ok (true unless (NaN = 0/0) < 0/0 < NaN)

View File

@@ -1,56 +0,0 @@
# Regular Expressions
# -------------------
#TODO: add some rigorous regex interpolation tests
test "basic regular expression literals", ->
ok 'a'.match(/a/)
ok 'a'.match /a/
ok 'a'.match(/a/g)
ok 'a'.match /a/g
test "division is not confused for a regular expression", ->
eq 2, 4 / 2 / 1
a = 4
b = 2
g = 1
eq 2, a / b/g
obj = method: -> 2
two = 2
eq 2, (obj.method()/two + obj.method()/two)
i = 1
eq 2, (4)/2/i
eq 1, i/i/i
test "backslash escapes", ->
eq "\\/\\\\", /\/\\/.source
test "#764: regular expressions should be indexable", ->
eq /0/['source'], ///#{0}///['source']
test "#584: slashes are allowed unescaped in character classes", ->
ok /^a\/[/]b$/.test 'a//b'
#### Heregexe(n|s)
test "a heregex will ignore whitespace and comments", ->
eq /^I'm\x20+[a]\s+Heregex?\/\/\//gim + '', ///
^ I'm \x20+ [a] \s+
Heregex? / // # or not
///gim + ''
test "heregex interpolation", ->
eq /\\#{}\\\"/ + '', ///
#{
"#{ '\\' }" # normal comment
}
# regex comment
\#{}
\\ \"
/// + ''
test "an empty heregex will compile to an empty, non-capturing group", ->
eq /(?:)/ + '', /// /// + ''

View File

@@ -1,372 +0,0 @@
# Test classes with a four-level inheritance chain.
class Base
func: (string) ->
"zero/#{string}"
@static: (string) ->
"static/#{string}"
class FirstChild extends Base
func: (string) ->
super('one/') + string
SecondChild = class extends FirstChild
func: (string) ->
super('two/') + string
thirdCtor = ->
@array = [1, 2, 3]
class ThirdChild extends SecondChild
constructor: -> thirdCtor.call this
# Gratuitous comment for testing.
func: (string) ->
super('three/') + string
result = (new ThirdChild).func 'four'
ok result is 'zero/one/two/three/four'
ok Base.static('word') is 'static/word'
FirstChild::func = (string) ->
super('one/').length + string
result = (new ThirdChild).func 'four'
ok result is '9two/three/four'
ok (new ThirdChild).array.join(' ') is '1 2 3'
identity = (f) -> f
class TopClass
constructor: (arg) ->
@prop = 'top-' + arg
class SuperClass extends TopClass
constructor: (arg) ->
identity super 'super-' + arg
class SubClass extends SuperClass
constructor: ->
identity super 'sub'
ok (new SubClass).prop is 'top-super-sub'
class OneClass
@new: 'new'
function: 'function'
constructor: (name) -> @name = name
class TwoClass extends OneClass
delete TwoClass.new
Function.prototype.new = -> new this arguments...
ok (TwoClass.new('three')).name is 'three'
ok (new OneClass).function is 'function'
ok OneClass.new is 'new'
delete Function.prototype.new
# And now the same tests, but written in the manual style:
Base = ->
Base::func = (string) ->
'zero/' + string
Base::['func-func'] = (string) ->
"dynamic-#{string}"
FirstChild = ->
SecondChild = ->
ThirdChild = ->
@array = [1, 2, 3]
this
ThirdChild extends SecondChild extends FirstChild extends Base
FirstChild::func = (string) ->
super('one/') + string
SecondChild::func = (string) ->
super('two/') + string
ThirdChild::func = (string) ->
super('three/') + string
result = (new ThirdChild).func 'four'
ok result is 'zero/one/two/three/four'
ok (new ThirdChild)['func-func']('thing') is 'dynamic-thing'
TopClass = (arg) ->
@prop = 'top-' + arg
this
SuperClass = (arg) ->
super 'super-' + arg
this
SubClass = ->
super 'sub'
this
SuperClass extends TopClass
SubClass extends SuperClass
ok (new SubClass).prop is 'top-super-sub'
# '@' referring to the current instance, and not being coerced into a call.
class ClassName
amI: ->
@ instanceof ClassName
obj = new ClassName
ok obj.amI()
# super() calls in constructors of classes that are defined as object properties.
class Hive
constructor: (name) -> @name = name
class Hive.Bee extends Hive
constructor: (name) -> super
maya = new Hive.Bee 'Maya'
ok maya.name is 'Maya'
# Class with JS-keyword properties.
class Class
class: 'class'
name: -> @class
instance = new Class
ok instance.class is 'class'
ok instance.name() is 'class'
# Classes with methods that are pre-bound to the instance.
# ... or statically, to the class.
class Dog
constructor: (name) ->
@name = name
bark: =>
"#{@name} woofs!"
@static = =>
new this('Dog')
spark = new Dog('Spark')
fido = new Dog('Fido')
fido.bark = spark.bark
ok fido.bark() is 'Spark woofs!'
obj = func: Dog.static
ok obj.func().name is 'Dog'
# Testing a bound function in a bound function.
class Mini
num: 10
generate: =>
for i in [1..3]
=>
@num
m = new Mini
eq (func() for func in m.generate()).join(' '), '10 10 10'
# Testing a contructor called with varargs.
class Connection
constructor: (one, two, three) ->
[@one, @two, @three] = [one, two, three]
out: ->
"#{@one}-#{@two}-#{@three}"
list = [3, 2, 1]
conn = new Connection list...
ok conn instanceof Connection
ok conn.out() is '3-2-1'
# Test calling super and passing along all arguments.
class Parent
method: (args...) -> @args = args
class Child extends Parent
method: -> super
c = new Child
c.method 1, 2, 3, 4
ok c.args.join(' ') is '1 2 3 4'
# Test classes wrapped in decorators.
func = (klass) ->
klass::prop = 'value'
klass
func class Test
prop2: 'value2'
ok (new Test).prop is 'value'
ok (new Test).prop2 is 'value2'
# Test anonymous classes.
obj =
klass: class
method: -> 'value'
instance = new obj.klass
ok instance.method() is 'value'
# Implicit objects as static properties.
class Static
@static =
one: 1
two: 2
ok Static.static.one is 1
ok Static.static.two is 2
# Nothing classes.
c = class
ok c instanceof Function
# Classes with value'd constructors.
counter = 0
classMaker = ->
counter += 1
inner = counter
->
@value = inner
class One
constructor: classMaker()
class Two
constructor: classMaker()
ok (new One).value is 1
ok (new Two).value is 2
ok (new One).value is 1
ok (new Two).value is 2
# Exectuable class bodies.
class A
if true
b: 'b'
else
c: 'c'
a = new A
eq a.b, 'b'
eq a.c, undefined
# Light metaprogramming.
class Base
@attr: (name) ->
@::[name] = (val) ->
if arguments.length > 0
@["_#{name}"] = val
else
@["_#{name}"]
class Robot extends Base
@attr 'power'
@attr 'speed'
robby = new Robot
ok robby.power() is undefined
robby.power 11
robby.speed Infinity
eq robby.power(), 11
eq robby.speed(), Infinity
# Namespaced classes do not reserve their function name in outside scope.
one = {}
two = {}
class one.Klass
@label = "one"
class two.Klass
@label = "two"
eq typeof Klass, 'undefined'
eq one.Klass.label, 'one'
eq two.Klass.label, 'two'
# Nested classes.
class Outer
constructor: ->
@label = 'outer'
class @Inner
constructor: ->
@label = 'inner'
eq (new Outer).label, 'outer'
eq (new Outer.Inner).label, 'inner'
# Variables in constructor bodies are correctly scoped.
class A
x = 1
constructor: ->
x = 10
y = 20
y = 2
captured: ->
{x, y}
a = new A
eq a.captured().x, 10
eq a.captured().y, 2
# Issue #924: Static methods in nested classes.
class A
@B: class
@c = -> 5
eq A.B.c(), 5
# `class extends this` ...
class A
func: -> 'A'
B = null
makeClass = ->
B = class extends this
func: -> super + ' B'
makeClass.call A
eq (new B()).func(), 'A B'

View File

@@ -1,318 +0,0 @@
# Basic array comprehensions.
nums = (n * n for n in [1, 2, 3] when n & 1)
results = (n * 2 for n in nums)
ok results.join(',') is '2,18'
# Basic object comprehensions.
obj = {one: 1, two: 2, three: 3}
names = (prop + '!' for prop of obj)
odds = (prop + '!' for prop, value of obj when value & 1)
ok names.join(' ') is "one! two! three!"
ok odds.join(' ') is "one! three!"
# Basic range comprehensions.
nums = (i * 3 for i in [1..3])
negs = (x for x in [-20..-5*2])
negs = negs[0..2]
result = nums.concat(negs).join(', ')
ok result is '3, 6, 9, -20, -19, -18'
# With range comprehensions, you can loop in steps.
results = (x for x in [0...15] by 5)
ok results.join(' ') is '0 5 10'
results = (x for x in [0..100] by 10)
ok results.join(' ') is '0 10 20 30 40 50 60 70 80 90 100'
# And can loop downwards, with a negative step.
results = (x for x in [5..1])
ok results.join(' ') is '5 4 3 2 1'
ok results.join(' ') is [(10-5)..(-2+3)].join(' ')
results = (x for x in [10..1])
ok results.join(' ') is [10..1].join(' ')
results = (x for x in [10...0] by -2)
ok results.join(' ') is [10, 8, 6, 4, 2].join(' ')
# Range comprehension gymnastics.
eq "#{i for i in [5..1]}", '5,4,3,2,1'
eq "#{i for i in [5..-5] by -5}", '5,0,-5'
a = 6
b = 0
c = -2
eq "#{i for i in [a..b]}", '6,5,4,3,2,1,0'
eq "#{i for i in [a..b] by c}", '6,4,2,0'
# Multiline array comprehension with filter.
evens = for num in [1, 2, 3, 4, 5, 6] when not (num & 1)
num *= -1
num -= 2
num * -1
eq evens + '', '4,6,8'
# The in operator still works, standalone.
ok 2 of evens
# all isn't reserved.
all = 1
# Ensure that the closure wrapper preserves local variables.
obj = {}
for method in ['one', 'two', 'three'] then do (method) ->
obj[method] = ->
"I'm " + method
ok obj.one() is "I'm one"
ok obj.two() is "I'm two"
ok obj.three() is "I'm three"
# Index values at the end of a loop.
i = 0
for i in [1..3]
-> 'func'
break if false
ok i is 4
# Ensure that local variables are closed over for range comprehensions.
funcs = for i in [1..3]
do (i) ->
-> -i
eq (func() for func in funcs).join(' '), '-1 -2 -3'
ok i is 4
# Even when referenced in the filter.
list = ['one', 'two', 'three']
methods = for num, i in list when num isnt 'two' and i isnt 1
do (num, i) ->
-> num + ' ' + i
ok methods.length is 2
ok methods[0]() is 'one 0'
ok methods[1]() is 'three 2'
# Even a convoluted one.
funcs = []
for i in [1..3]
do (i) ->
x = i * 2
((z)->
funcs.push -> z + ' ' + i
)(x)
ok (func() for func in funcs).join(', ') is '2 1, 4 2, 6 3'
funcs = []
results = for i in [1..3]
do (i) ->
z = (x * 3 for x in [1..i])
((a, b, c) -> [a, b, c].join(' ')).apply this, z
ok results.join(', ') is '3 , 3 6 , 3 6 9'
# Naked ranges are expanded into arrays.
array = [0..10]
ok(num % 2 is 0 for num in array by 2)
# Nested shared scopes.
foo = ->
for i in [0..7]
do (i) ->
for j in [0..7]
do (j) ->
-> i + j
eq foo()[3][4](), 7
# Scoped loop pattern matching.
a = [[0], [1]]
funcs = []
for [v] in a
do (v) ->
funcs.push -> v
eq funcs[0](), 0
eq funcs[1](), 1
# Nested comprehensions.
multiLiner =
for x in [3..5]
for y in [3..5]
[x, y]
singleLiner =
(([x, y] for y in [3..5]) for x in [3..5])
ok multiLiner.length is singleLiner.length
ok 5 is multiLiner[2][2][1]
ok 5 is singleLiner[2][2][1]
# Comprehensions within parentheses.
result = null
store = (obj) -> result = obj
store (x * 2 for x in [3, 2, 1])
ok result.join(' ') is '6 4 2'
# Closure-wrapped comprehensions that refer to the "arguments" object.
expr = ->
result = (item * item for item in arguments)
ok expr(2, 4, 8).join(' ') is '4 16 64'
# Fast object comprehensions over all properties, including prototypal ones.
class Cat
constructor: -> @name = 'Whiskers'
breed: 'tabby'
hair: 'cream'
whiskers = new Cat
own = (value for own key, value of whiskers)
all = (value for key, value of whiskers)
ok own.join(' ') is 'Whiskers'
ok all.sort().join(' ') is 'Whiskers cream tabby'
# Optimized range comprehensions.
exxes = ('x' for [0...10])
ok exxes.join(' ') is 'x x x x x x x x x x'
# Comprehensions safely redeclare parameters if they're not present in closest
# scope.
rule = (x) -> x
learn = ->
rule for rule in [1, 2, 3]
ok learn().join(' ') is '1 2 3'
ok rule(101) is 101
f = -> [-> ok no, 'should cache source']
ok yes for k of [f] = f()
# Lenient on pure statements not trying to reach out of the closure
val = for i in [1]
for j in [] then break
i
ok val[0] is i
# Comprehensions only wrap their last line in a closure, allowing other lines
# to have pure expressions in them.
func = -> for i in [1]
break if i is 2
j for j in [1]
ok func()[0][0] is 1
i = 6
odds = while i--
continue unless i & 1
i
ok odds.join(', ') is '5, 3, 1'
# Issue #897: Ensure that plucked function variables aren't leaked.
facets = {}
list = ['one', 'two']
(->
for entity in list
facets[entity] = -> entity
)()
eq typeof entity, 'undefined'
eq facets['two'](), 'two'
# Issue #905. Soaks as the for loop subject.
a = {b: {c: [1, 2, 3]}}
for d in a.b?.c
e = d
eq e, 3
# Issue #948. Capturing loop variables.
funcs = []
list = ->
[1, 2, 3]
for y in list()
do (y) ->
z = y
funcs.push -> "y is #{y} and z is #{z}"
eq funcs[1](), "y is 2 and z is 2"
# Cancel the comprehension if there's a jump inside the loop.
result = try
for i in [0...10]
continue if i < 5
i
eq result, 10
# Comprehensions over break.
arrayEq (break for [1..10]), []
# Comprehensions over continue.
arrayEq (break for [1..10]), []
# Comprehensions over function literals.
a = 0
for f in [-> a = 1]
do (f) ->
do f
eq a, 1
# Comprehensions that mention arguments.
list = [arguments: 10]
args = for f in list
do (f) ->
f.arguments
eq args[0], 10

View File

@@ -1,111 +0,0 @@
a = """
basic heredoc
on two lines
"""
ok a is "basic heredoc\non two lines"
a = '''
a
"b
c
'''
ok a is "a\n \"b\nc"
a = """
a
b
c
"""
ok a is "a\n b\n c"
a = '''one-liner'''
ok a is 'one-liner'
a = """
out
here
"""
ok a is "out\nhere"
a = '''
a
b
c
'''
ok a is " a\n b\nc"
a = '''
a
b c
'''
ok a is "a\n\n\nb c"
a = '''more"than"one"quote'''
ok a is 'more"than"one"quote'
val = 10
a = """
basic heredoc #{val}
on two lines
"""
b = '''
basic heredoc #{val}
on two lines
'''
ok a is "basic heredoc 10\non two lines"
ok b is "basic heredoc \#{val}\non two lines"
a = '''here's an apostrophe'''
ok a is "here's an apostrophe"
# The indentation detector ignores blank lines without trailing whitespace
a = """
one
two
"""
ok a is "one\ntwo\n"
eq ''' line 0
should not be relevant
to the indent level
''', '
line 0\n
should not be relevant\n
to the indent level
'
eq ''' '\\\' ''', " '\\' "
eq """ "\\\" """, ' "\\" '
eq ''' <- keep these spaces -> ''', ' <- keep these spaces -> '
eq 'multiline nested "interpolations" work', """multiline #{
"nested #{(->
ok yes
"\"interpolations\""
)()}"
} work"""

View File

@@ -1,27 +0,0 @@
# Ensure that the OptionParser handles arguments correctly.
return unless require?
{OptionParser} = require './../lib/optparse'
opt = new OptionParser [
['-r', '--required [DIR]', 'desc required']
['-o', '--optional', 'desc optional']
['-l', '--list [FILES*]', 'desc list']
]
result = opt.parse ['one', 'two', 'three', '-r', 'dir']
ok result.arguments.length is 5
ok result.arguments[3] is '-r'
result = opt.parse ['--optional', '-r', 'folder', 'one', 'two']
ok result.optional is true
ok result.required is 'folder'
ok result.arguments.join(' ') is 'one two'
result = opt.parse ['-l', 'one.txt', '-l', 'two.txt', 'three']
ok result.list instanceof Array
ok result.list.join(' ') is 'one.txt two.txt'
ok result.arguments.join(' ') is 'three'

View File

@@ -1,118 +0,0 @@
eq '(((dollars)))', '\(\(\(dollars\)\)\)'
eq 'one two three', "one
two
three"
eq "four five", 'four
five'
#647
eq "''Hello, World\\''", '''
'\'Hello, World\\\''
'''
eq '""Hello, World\\""', """
"\"Hello, World\\\""
"""
eq 'Hello, World\n', '''
Hello, World\
'''
hello = 'Hello'
world = 'World'
ok '#{hello} #{world}!' is '#{hello} #{world}!'
ok "#{hello} #{world}!" is 'Hello World!'
ok "[#{hello}#{world}]" is '[HelloWorld]'
ok "#{hello}##{world}" is 'Hello#World'
ok "Hello #{ 1 + 2 } World" is 'Hello 3 World'
ok "#{hello} #{ 1 + 2 } #{world}" is "Hello 3 World"
[s, t, r, i, n, g] = ['s', 't', 'r', 'i', 'n', 'g']
ok "#{s}#{t}#{r}#{i}#{n}#{g}" is 'string'
ok "\#{s}\#{t}\#{r}\#{i}\#{n}\#{g}" is '#{s}#{t}#{r}#{i}#{n}#{g}'
ok "\#{string}" is '#{string}'
ok "\#{Escaping} first" is '#{Escaping} first'
ok "Escaping \#{in} middle" is 'Escaping #{in} middle'
ok "Escaping \#{last}" is 'Escaping #{last}'
ok "##" is '##'
ok "#{}" is ''
ok "#{}A#{} #{} #{}B#{}" is 'A B'
ok "\\\#{}" is '\\#{}'
ok "I won ##{20} last night." is 'I won #20 last night.'
ok "I won ##{'#20'} last night." is 'I won ##20 last night.'
ok "#{hello + world}" is 'HelloWorld'
ok "#{hello + ' ' + world + '!'}" is 'Hello World!'
list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
ok "values: #{list.join(', ')}, length: #{list.length}." is 'values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, length: 10.'
ok "values: #{list.join ' '}" is 'values: 0 1 2 3 4 5 6 7 8 9'
obj = {
name: 'Joe'
hi: -> "Hello #{@name}."
cya: -> "Hello #{@name}.".replace('Hello','Goodbye')
}
ok obj.hi() is "Hello Joe."
ok obj.cya() is "Goodbye Joe."
ok "With #{"quotes"}" is 'With quotes'
ok 'With #{"quotes"}' is 'With #{"quotes"}'
ok "Where is #{obj["name"] + '?'}" is 'Where is Joe?'
ok "Where is #{"the nested #{obj["name"]}"}?" is 'Where is the nested Joe?'
ok "Hello #{world ? "#{hello}"}" is 'Hello World'
ok "Hello #{"#{"#{obj["name"]}" + '!'}"}" is 'Hello Joe!'
a = """
Hello #{ "Joe" }
"""
ok a is "Hello Joe"
a = 1
b = 2
c = 3
ok "#{a}#{b}#{c}" is '123'
result = null
stash = (str) -> result = str
stash "a #{ ('aa').replace /a/g, 'b' } c"
ok result is 'a bb c'
foo = "hello"
ok "#{foo.replace("\"", "")}" is 'hello'
eq 'multiline nested "interpolations" work', """multiline #{
"nested #{
ok true
"\"interpolations\""
}"
} work"""
# Issue #923: Tricky interpolation.
eq "#{ "{" }", "{"
eq "#{ '#{}}' } }", '#{}} }'
eq "#{"'#{ ({a: "b#{1}"}['a']) }'"}", "'b1'"

View File

@@ -1,103 +0,0 @@
num = 10
result = switch num
when 5 then false
when 'a'
true
true
false
when 10 then true
# Mid-switch comment with whitespace
# and multi line
when 11 then false
else false
ok result
func = (num) ->
switch num
when 2, 4, 6
true
when 1, 3, 5
false
ok func(2)
ok func(6)
ok !func(3)
eq func(8), undefined
# Ensure that trailing switch elses don't get rewritten.
result = false
switch "word"
when "one thing"
doSomething()
else
result = true unless false
ok result
result = false
switch "word"
when "one thing"
doSomething()
when "other thing"
doSomething()
else
result = true unless false
ok result
# Should be able to handle switches sans-condition.
result = switch
when null then 0
when !1 then 1
when '' not of {''} then 2
when [] not instanceof Array then 3
when true is false then 4
when 'x' < 'y' > 'z' then 5
when 'a' in ['b', 'c'] then 6
when 'd' in (['e', 'f']) then 7
else ok
eq result, ok
# Should be able to use "@properties" within the switch clause.
obj = {
num: 101
func: ->
switch @num
when 101 then '101!'
else 'other'
}
ok obj.func() is '101!'
# Should be able to use "@properties" within the switch cases.
obj = {
num: 101
func: (yesOrNo) ->
result = switch yesOrNo
when yes then @num
else 'other'
result
}
ok obj.func(yes) is 101
# Switch with break as the return value of a loop.
i = 10
results = while i > 0
i--
switch i % 2
when 1 then i
when 0 then break
eq results.join(', '), '9, , 7, , 5, , 3, , 1, '

View File

@@ -1,71 +0,0 @@
i = 5
list = while i -= 1
i * 2
ok list.join(' ') is "8 6 4 2"
i = 5
list = (i * 3 while i -= 1)
ok list.join(' ') is "12 9 6 3"
i = 5
func = (num) -> i -= num
assert = -> ok i < 5 > 0
results = while func 1
assert()
i
ok results.join(' ') is '4 3 2 1'
i = 10
results = while i -= 1 when i % 2 is 0
i * 2
ok results.join(' ') is '16 12 8 4'
value = false
i = 0
results = until value
value = true if i is 5
i += 1
ok i is 6
# And, the loop form of while.
i = 5
list = []
loop
i -= 1
break if i is 0
list.push i * 2
ok list.join(' ') is '8 6 4 2'
#759: `if` within `while` condition
2 while if 1 then 0
# While over break.
i = 0
result = while i < 10
i++
break
arrayEq result, []
# While over continue.
i = 0
result = while i < 10
i++
continue
arrayEq result, []

View File

@@ -0,0 +1,62 @@
# Assignment
# ----------
test "context property assignment (using @)", ->
nonce = {}
addMethod = ->
@method = -> nonce
this
eq nonce, addMethod.call({}).method()
test "unassignable values", ->
nonce = {}
for nonref in ['', '""', '0', 'f()'].concat CoffeeScript.RESERVED
eq nonce, (try CoffeeScript.compile "#{nonref} = v" catch e then nonce)
test "compound assignments should not declare", ->
# TODO: make description more clear
# TODO: remove reference to Math
eq Math, (-> Math or= 0)()
#### Compound Assignment
test "compound assignment (math operators)", ->
num = 10
num -= 5
eq 5, num
num *= 10
eq 50, num
num /= 10
eq 5, num
num %= 3
eq 2, num
test "more compound assignment", ->
a = {}
val = undefined
val ||= a
val ||= true
eq a, val
b = {}
val &&= true
eq val, true
val &&= b
eq b, val
c = {}
val = null
val ?= c
val ?= true
eq c, val
#### Destructuring Assignment
# NO TESTS?!
# TODO: make tests for destructuring assignment

View File

@@ -0,0 +1,377 @@
# Classes
# -------
# TODO: refactor class tests
# Test classes with a four-level inheritance chain.
class Base
func: (string) ->
"zero/#{string}"
@static: (string) ->
"static/#{string}"
class FirstChild extends Base
func: (string) ->
super('one/') + string
SecondChild = class extends FirstChild
func: (string) ->
super('two/') + string
thirdCtor = ->
@array = [1, 2, 3]
class ThirdChild extends SecondChild
constructor: -> thirdCtor.call this
# Gratuitous comment for testing.
func: (string) ->
super('three/') + string
result = (new ThirdChild).func 'four'
ok result is 'zero/one/two/three/four'
ok Base.static('word') is 'static/word'
FirstChild::func = (string) ->
super('one/').length + string
result = (new ThirdChild).func 'four'
ok result is '9two/three/four'
ok (new ThirdChild).array.join(' ') is '1 2 3'
identity = (f) -> f
class TopClass
constructor: (arg) ->
@prop = 'top-' + arg
class SuperClass extends TopClass
constructor: (arg) ->
identity super 'super-' + arg
class SubClass extends SuperClass
constructor: ->
identity super 'sub'
ok (new SubClass).prop is 'top-super-sub'
class OneClass
@new: 'new'
function: 'function'
constructor: (name) -> @name = name
class TwoClass extends OneClass
delete TwoClass.new
Function.prototype.new = -> new this arguments...
ok (TwoClass.new('three')).name is 'three'
ok (new OneClass).function is 'function'
ok OneClass.new is 'new'
delete Function.prototype.new
# And now the same tests, but written in the manual style:
Base = ->
Base::func = (string) ->
'zero/' + string
Base::['func-func'] = (string) ->
"dynamic-#{string}"
FirstChild = ->
SecondChild = ->
ThirdChild = ->
@array = [1, 2, 3]
this
ThirdChild extends SecondChild extends FirstChild extends Base
FirstChild::func = (string) ->
super('one/') + string
SecondChild::func = (string) ->
super('two/') + string
ThirdChild::func = (string) ->
super('three/') + string
result = (new ThirdChild).func 'four'
ok result is 'zero/one/two/three/four'
ok (new ThirdChild)['func-func']('thing') is 'dynamic-thing'
TopClass = (arg) ->
@prop = 'top-' + arg
this
SuperClass = (arg) ->
super 'super-' + arg
this
SubClass = ->
super 'sub'
this
SuperClass extends TopClass
SubClass extends SuperClass
ok (new SubClass).prop is 'top-super-sub'
# '@' referring to the current instance, and not being coerced into a call.
class ClassName
amI: ->
@ instanceof ClassName
obj = new ClassName
ok obj.amI()
# super() calls in constructors of classes that are defined as object properties.
class Hive
constructor: (name) -> @name = name
class Hive.Bee extends Hive
constructor: (name) -> super
maya = new Hive.Bee 'Maya'
ok maya.name is 'Maya'
# Class with JS-keyword properties.
class Class
class: 'class'
name: -> @class
instance = new Class
ok instance.class is 'class'
ok instance.name() is 'class'
# Classes with methods that are pre-bound to the instance.
# ... or statically, to the class.
class Dog
constructor: (name) ->
@name = name
bark: =>
"#{@name} woofs!"
@static = =>
new this('Dog')
spark = new Dog('Spark')
fido = new Dog('Fido')
fido.bark = spark.bark
ok fido.bark() is 'Spark woofs!'
obj = func: Dog.static
ok obj.func().name is 'Dog'
# Testing a bound function in a bound function.
class Mini
num: 10
generate: =>
for i in [1..3]
=>
@num
m = new Mini
eq (func() for func in m.generate()).join(' '), '10 10 10'
# Testing a contructor called with varargs.
class Connection
constructor: (one, two, three) ->
[@one, @two, @three] = [one, two, three]
out: ->
"#{@one}-#{@two}-#{@three}"
list = [3, 2, 1]
conn = new Connection list...
ok conn instanceof Connection
ok conn.out() is '3-2-1'
# Test calling super and passing along all arguments.
class Parent
method: (args...) -> @args = args
class Child extends Parent
method: -> super
c = new Child
c.method 1, 2, 3, 4
ok c.args.join(' ') is '1 2 3 4'
# Test classes wrapped in decorators.
func = (klass) ->
klass::prop = 'value'
klass
func class Test
prop2: 'value2'
ok (new Test).prop is 'value'
ok (new Test).prop2 is 'value2'
# Test anonymous classes.
obj =
klass: class
method: -> 'value'
instance = new obj.klass
ok instance.method() is 'value'
# Implicit objects as static properties.
class Static
@static =
one: 1
two: 2
ok Static.static.one is 1
ok Static.static.two is 2
# Nothing classes.
c = class
ok c instanceof Function
# Classes with value'd constructors.
counter = 0
classMaker = ->
counter += 1
inner = counter
->
@value = inner
class One
constructor: classMaker()
class Two
constructor: classMaker()
ok (new One).value is 1
ok (new Two).value is 2
ok (new One).value is 1
ok (new Two).value is 2
# Exectuable class bodies.
class A
if true
b: 'b'
else
c: 'c'
a = new A
eq a.b, 'b'
eq a.c, undefined
# Light metaprogramming.
class Base
@attr: (name) ->
@::[name] = (val) ->
if arguments.length > 0
@["_#{name}"] = val
else
@["_#{name}"]
class Robot extends Base
@attr 'power'
@attr 'speed'
robby = new Robot
ok robby.power() is undefined
robby.power 11
robby.speed Infinity
eq robby.power(), 11
eq robby.speed(), Infinity
# Namespaced classes do not reserve their function name in outside scope.
one = {}
two = {}
class one.Klass
@label = "one"
class two.Klass
@label = "two"
eq typeof Klass, 'undefined'
eq one.Klass.label, 'one'
eq two.Klass.label, 'two'
# Nested classes.
class Outer
constructor: ->
@label = 'outer'
class @Inner
constructor: ->
@label = 'inner'
eq (new Outer).label, 'outer'
eq (new Outer.Inner).label, 'inner'
# Variables in constructor bodies are correctly scoped.
class A
x = 1
constructor: ->
x = 10
y = 20
y = 2
captured: ->
{x, y}
a = new A
eq a.captured().x, 10
eq a.captured().y, 2
# Issue #924: Static methods in nested classes.
class A
@B: class
@c = -> 5
eq A.B.c(), 5
# `class extends this` ...
class A
func: -> 'A'
B = null
makeClass = ->
B = class extends this
func: -> super + ' B'
makeClass.call A
eq (new B()).func(), 'A B'

View File

@@ -0,0 +1,201 @@
# Comments
# --------
# Note: awkward spacing seen in some tests is likely intentional.
test "comments in objects", ->
obj1 = {
# comment
# comment
# comment
one: 1
# comment
two: 2
# comment
}
ok Object::hasOwnProperty.call(obj1,'one')
eq obj1.one, 1
ok Object::hasOwnProperty.call(obj1,'two')
eq obj1.two, 2
test "comments in YAML-style objects", ->
obj2 =
# comment
# comment
# comment
three: 3
# comment
four: 4
# comment
ok Object::hasOwnProperty.call(obj2,'three')
eq obj2.three, 3
ok Object::hasOwnProperty.call(obj2,'four')
eq obj2.four, 4
test "comments following operators that continue lines", ->
sum =
1 +
1 + # comment
1
eq 3, sum
test "comments in functions", ->
fn = ->
# comment
false
false # comment
false
# comment
# comment
true
ok fn()
fn2 = -> #comment
fn()
# comment
ok fn2()
test "trailing comment before an outdent", ->
nonce = {}
fn3 = ->
if true
undefined # comment
nonce
eq nonce, fn3()
test "comments in a switch", ->
nonce = {}
result = switch nonce #comment
# comment
when false then undefined
# comment
when null #comment
undefined
else nonce # comment
eq nonce, result
test "comment with conditional statements", ->
nonce = {}
result = if false # comment
undefined
#comment
else # comment
nonce
# comment
eq nonce, result
test "spaced comments with conditional statements", ->
nonce = {}
result = if false
undefined
# comment
else if false
undefined
# comment
else
nonce
eq nonce, result
#### Block Comments
###
This is a here-comment.
Kind of like a heredoc.
###
test "block comments in objects", ->
a = {}
b = {}
obj = {
a: a
###
comment
###
b: b
}
eq a, obj.a
eq b, obj.b
test "block comments in YAML-style", ->
a = {}
b = {}
obj =
a: a
###
comment
###
b: b
eq a, obj.a
eq b, obj.b
test "block comments in functions", ->
nonce = {}
fn1 = ->
true
###
false
###
ok fn1()
fn2 = ->
###
block comment
###
nonce
eq nonce, fn2()
fn3 = ->
nonce
###
block comment
###
eq nonce, fn3()
fn4 = ->
one = ->
###
block comment
###
two = ->
three = ->
nonce
eq nonce, fn4()()()()
test "block comments inside class bodies", ->
class A
a: ->
###
Comment
###
b: ->
ok A.prototype.b instanceof Function
class B
###
Comment
###
a: ->
b: ->
ok B.prototype.a instanceof Function

View File

@@ -0,0 +1,323 @@
# Comprehensions
# --------------
# TODO: refactor comprehension tests
# Basic array comprehensions.
nums = (n * n for n in [1, 2, 3] when n & 1)
results = (n * 2 for n in nums)
ok results.join(',') is '2,18'
# Basic object comprehensions.
obj = {one: 1, two: 2, three: 3}
names = (prop + '!' for prop of obj)
odds = (prop + '!' for prop, value of obj when value & 1)
ok names.join(' ') is "one! two! three!"
ok odds.join(' ') is "one! three!"
# Basic range comprehensions.
nums = (i * 3 for i in [1..3])
negs = (x for x in [-20..-5*2])
negs = negs[0..2]
result = nums.concat(negs).join(', ')
ok result is '3, 6, 9, -20, -19, -18'
# With range comprehensions, you can loop in steps.
results = (x for x in [0...15] by 5)
ok results.join(' ') is '0 5 10'
results = (x for x in [0..100] by 10)
ok results.join(' ') is '0 10 20 30 40 50 60 70 80 90 100'
# And can loop downwards, with a negative step.
results = (x for x in [5..1])
ok results.join(' ') is '5 4 3 2 1'
ok results.join(' ') is [(10-5)..(-2+3)].join(' ')
results = (x for x in [10..1])
ok results.join(' ') is [10..1].join(' ')
results = (x for x in [10...0] by -2)
ok results.join(' ') is [10, 8, 6, 4, 2].join(' ')
# Range comprehension gymnastics.
eq "#{i for i in [5..1]}", '5,4,3,2,1'
eq "#{i for i in [5..-5] by -5}", '5,0,-5'
a = 6
b = 0
c = -2
eq "#{i for i in [a..b]}", '6,5,4,3,2,1,0'
eq "#{i for i in [a..b] by c}", '6,4,2,0'
# Multiline array comprehension with filter.
evens = for num in [1, 2, 3, 4, 5, 6] when not (num & 1)
num *= -1
num -= 2
num * -1
eq evens + '', '4,6,8'
# The in operator still works, standalone.
ok 2 of evens
# all isn't reserved.
all = 1
# Ensure that the closure wrapper preserves local variables.
obj = {}
for method in ['one', 'two', 'three'] then do (method) ->
obj[method] = ->
"I'm " + method
ok obj.one() is "I'm one"
ok obj.two() is "I'm two"
ok obj.three() is "I'm three"
# Index values at the end of a loop.
i = 0
for i in [1..3]
-> 'func'
break if false
ok i is 4
# Ensure that local variables are closed over for range comprehensions.
funcs = for i in [1..3]
do (i) ->
-> -i
eq (func() for func in funcs).join(' '), '-1 -2 -3'
ok i is 4
# Even when referenced in the filter.
list = ['one', 'two', 'three']
methods = for num, i in list when num isnt 'two' and i isnt 1
do (num, i) ->
-> num + ' ' + i
ok methods.length is 2
ok methods[0]() is 'one 0'
ok methods[1]() is 'three 2'
# Even a convoluted one.
funcs = []
for i in [1..3]
do (i) ->
x = i * 2
((z)->
funcs.push -> z + ' ' + i
)(x)
ok (func() for func in funcs).join(', ') is '2 1, 4 2, 6 3'
funcs = []
results = for i in [1..3]
do (i) ->
z = (x * 3 for x in [1..i])
((a, b, c) -> [a, b, c].join(' ')).apply this, z
ok results.join(', ') is '3 , 3 6 , 3 6 9'
# Naked ranges are expanded into arrays.
array = [0..10]
ok(num % 2 is 0 for num in array by 2)
# Nested shared scopes.
foo = ->
for i in [0..7]
do (i) ->
for j in [0..7]
do (j) ->
-> i + j
eq foo()[3][4](), 7
# Scoped loop pattern matching.
a = [[0], [1]]
funcs = []
for [v] in a
do (v) ->
funcs.push -> v
eq funcs[0](), 0
eq funcs[1](), 1
# Nested comprehensions.
multiLiner =
for x in [3..5]
for y in [3..5]
[x, y]
singleLiner =
(([x, y] for y in [3..5]) for x in [3..5])
ok multiLiner.length is singleLiner.length
ok 5 is multiLiner[2][2][1]
ok 5 is singleLiner[2][2][1]
# Comprehensions within parentheses.
result = null
store = (obj) -> result = obj
store (x * 2 for x in [3, 2, 1])
ok result.join(' ') is '6 4 2'
# Closure-wrapped comprehensions that refer to the "arguments" object.
expr = ->
result = (item * item for item in arguments)
ok expr(2, 4, 8).join(' ') is '4 16 64'
# Fast object comprehensions over all properties, including prototypal ones.
class Cat
constructor: -> @name = 'Whiskers'
breed: 'tabby'
hair: 'cream'
whiskers = new Cat
own = (value for own key, value of whiskers)
all = (value for key, value of whiskers)
ok own.join(' ') is 'Whiskers'
ok all.sort().join(' ') is 'Whiskers cream tabby'
# Optimized range comprehensions.
exxes = ('x' for [0...10])
ok exxes.join(' ') is 'x x x x x x x x x x'
# Comprehensions safely redeclare parameters if they're not present in closest
# scope.
rule = (x) -> x
learn = ->
rule for rule in [1, 2, 3]
ok learn().join(' ') is '1 2 3'
ok rule(101) is 101
f = -> [-> ok no, 'should cache source']
ok yes for k of [f] = f()
# Lenient on pure statements not trying to reach out of the closure
val = for i in [1]
for j in [] then break
i
ok val[0] is i
# Comprehensions only wrap their last line in a closure, allowing other lines
# to have pure expressions in them.
func = -> for i in [1]
break if i is 2
j for j in [1]
ok func()[0][0] is 1
i = 6
odds = while i--
continue unless i & 1
i
ok odds.join(', ') is '5, 3, 1'
# Issue #897: Ensure that plucked function variables aren't leaked.
facets = {}
list = ['one', 'two']
(->
for entity in list
facets[entity] = -> entity
)()
eq typeof entity, 'undefined'
eq facets['two'](), 'two'
# Issue #905. Soaks as the for loop subject.
a = {b: {c: [1, 2, 3]}}
for d in a.b?.c
e = d
eq e, 3
# Issue #948. Capturing loop variables.
funcs = []
list = ->
[1, 2, 3]
for y in list()
do (y) ->
z = y
funcs.push -> "y is #{y} and z is #{z}"
eq funcs[1](), "y is 2 and z is 2"
# Cancel the comprehension if there's a jump inside the loop.
result = try
for i in [0...10]
continue if i < 5
i
eq result, 10
# Comprehensions over break.
arrayEq (break for [1..10]), []
# Comprehensions over continue.
arrayEq (break for [1..10]), []
# Comprehensions over function literals.
a = 0
for f in [-> a = 1]
do (f) ->
do f
eq a, 1
# Comprehensions that mention arguments.
list = [arguments: 10]
args = for f in list
do (f) ->
f.arguments
eq args[0], 10

View File

@@ -0,0 +1,396 @@
# Control Flow
# ------------
# shared identity function
id = (_) -> if arguments.length is 1 then _ else Array::slice.call(arguments)
#### Conditionals
test "basic conditionals", ->
if false
ok false
else if false
ok false
else
ok true
if true
ok true
else if true
ok false
else
ok true
unless true
ok false
else unless true
ok false
else
ok true
unless false
ok true
else unless false
ok false
else
ok true
test "single-line conditional", ->
if false then ok false else ok true
unless false then ok true else ok false
test "nested conditionals", ->
nonce = {}
eq nonce, (if true
unless false
if false then false else
if true
nonce)
test "nested single-line conditionals", ->
nonce = {}
a = if false then undefined else b = if 0 then undefined else nonce
eq nonce, a
eq nonce, b
c = if false then undefined else (if 0 then undefined else nonce)
eq nonce, c
d = if true then id(if false then undefined else nonce)
eq nonce, d
test "empty conditional bodies", ->
eq undefined, (if false
else if false
else)
test "conditional bodies containing only comments", ->
eq undefined, (if true
###
block comment
###
else
# comment
)
eq undefined, (if false
# comment
else if true
###
block comment
###
else)
test "return value of if-else is from the proper body", ->
nonce = {}
eq nonce, if false then undefined else nonce
test "return value of unless-else is from the proper body", ->
nonce = {}
eq nonce, unless true then undefined else nonce
test "assign inside the condition of a conditional statement", ->
nonce = {}
if a = nonce then 1
eq nonce, a
1 if b = nonce
eq nonce, b
# Interactions With Functions
test "single-line function definition with single-line conditional", ->
fn = -> if 1 < 0.5 then 1 else -1
ok fn() is -1
test "function resturns conditional value with no `else`", ->
fn = ->
return if false then true
eq undefined, fn()
test "function returns a conditional value", ->
a = {}
fnA = ->
return if false then undefined else a
eq a, fnA()
b = {}
fnB = ->
return unless false then b else undefined
eq b, fnB()
test "passing a conditional value to a function", ->
nonce = {}
eq nonce, id if false then undefined else nonce
test "unmatched `then` should catch implicit calls", ->
a = 0
trueFn = -> true
if trueFn undefined then a += 1
eq 1, a
# if-to-ternary
test "if-to-ternary with instanceof requires parentheses", ->
nonce = {}
eq nonce, (if {} instanceof Object
nonce
else
undefined)
test "if-to-ternary as part of a larger operation requires parentheses", ->
ok 2, 1 + if false then 0 else 1
# Odd Formatting
test "if-else indented within an assignment", ->
nonce = {}
result =
if false
undefined
else
nonce
eq nonce, result
test "suppressed indentation via assignment", ->
nonce = {}
result =
if false then undefined
else if no then undefined
else if 0 then undefined
else if 1 < 0 then undefined
else id(
if false then undefined
else nonce
)
eq nonce, result
test "tight formatting with leading `then`", ->
nonce = {}
eq nonce,
if true
then nonce
else undefined
test "#738", ->
nonce = {}
fn = if true then -> nonce
eq nonce, fn()
test "#748: trailing reserved identifiers", ->
nonce = {}
obj = delete: true
result = if obj.delete
nonce
eq nonce, result
#### For / While / Until / Loop
# TODO: refactor while tests
# While
i = 5
list = while i -= 1
i * 2
ok list.join(' ') is "8 6 4 2"
i = 5
list = (i * 3 while i -= 1)
ok list.join(' ') is "12 9 6 3"
i = 5
func = (num) -> i -= num
assert = -> ok i < 5 > 0
results = while func 1
assert()
i
ok results.join(' ') is '4 3 2 1'
i = 10
results = while i -= 1 when i % 2 is 0
i * 2
ok results.join(' ') is '16 12 8 4'
#759: `if` within `while` condition
2 while if 1 then 0
test "assignment inside the condition of a `while` loop", ->
nonce = {}
count = 1
a = nonce while count--
eq nonce, a
count = 1
while count--
b = nonce
eq nonce, b
# While over break.
i = 0
result = while i < 10
i++
break
arrayEq result, []
# While over continue.
i = 0
result = while i < 10
i++
continue
arrayEq result, []
# Until
# TODO: refactor until tests
# TODO: add until tests
value = false
i = 0
results = until value
value = true if i is 5
i += 1
ok i is 6
# Loop
# TODO: refactor loop tests
# TODO: add loop tests
i = 5
list = []
loop
i -= 1
break if i is 0
list.push i * 2
ok list.join(' ') is '8 6 4 2'
# TODO: refactor for tests
# TODO: add for tests
test "break at the top level", ->
for i in [1,2,3]
result = i
if i == 2
break
eq 2, result
test "break *not* at the top level", ->
someFunc = () ->
i = 0
while ++i < 3
result = i
break if i > 1
result
eq 2, someFunc()
#### Switch
# TODO: refactor switch tests
num = 10
result = switch num
when 5 then false
when 'a'
true
true
false
when 10 then true
# Mid-switch comment with whitespace
# and multi line
when 11 then false
else false
ok result
func = (num) ->
switch num
when 2, 4, 6
true
when 1, 3, 5
false
ok func(2)
ok func(6)
ok !func(3)
eq func(8), undefined
# Ensure that trailing switch elses don't get rewritten.
result = false
switch "word"
when "one thing"
doSomething()
else
result = true unless false
ok result
result = false
switch "word"
when "one thing"
doSomething()
when "other thing"
doSomething()
else
result = true unless false
ok result
# Should be able to handle switches sans-condition.
result = switch
when null then 0
when !1 then 1
when '' not of {''} then 2
when [] not instanceof Array then 3
when true is false then 4
when 'x' < 'y' > 'z' then 5
when 'a' in ['b', 'c'] then 6
when 'd' in (['e', 'f']) then 7
else ok
eq result, ok
# Should be able to use "@properties" within the switch clause.
obj = {
num: 101
func: ->
switch @num
when 101 then '101!'
else 'other'
}
ok obj.func() is '101!'
# Should be able to use "@properties" within the switch cases.
obj = {
num: 101
func: (yesOrNo) ->
result = switch yesOrNo
when yes then @num
else 'other'
result
}
ok obj.func(yes) is 101
# Switch with break as the return value of a loop.
i = 10
results = while i > 0
i--
switch i % 2
when 1 then i
when 0 then break
eq results.join(', '), '9, , 7, , 5, , 3, , 1, '

View File

@@ -0,0 +1,90 @@
# Exception Handling
# ------------------
# shared nonce
nonce = {}
#### Throw
test "basic exception throwing", ->
throws (-> throw 'error'), 'error'
#### Empty Try/Catch/Finally
test "try can exist alone", ->
try
test "try/catch with empty try, empty catch", ->
try
# nothing
catch err
# nothing
test "single-line try/catch with empty try, empty catch", ->
try catch err
test "try/finally with empty try, empty finally", ->
try
# nothing
finally
# nothing
test "single-line try/finally with empty try, empty finally", ->
try finally
test "try/catch/finally with empty try, empty catch, empty finally", ->
try
catch err
finally
test "single-line try/catch/finally with empty try, empty catch, empty finally", ->
try catch err then finally
#### Try/Catch/Finally as an Expression
test "return the result of try when no exception is thrown", ->
result = try
nonce
catch err
undefined
finally
undefined
eq nonce, result
test "single-line result of try when no exception is thrown", ->
result = try nonce catch err then undefined
eq nonce, result
test "return the result of catch when an exception is thrown", ->
fn = ->
try
throw ->
catch err
nonce
doesNotThrow fn
eq nonce, fn()
test "single-line result of catch when an exception is thrown", ->
fn = ->
try throw (->) catch err then nonce
doesNotThrow fn
eq nonce, fn()
test "optional catch", ->
fn = ->
try throw ->
nonce
doesNotThrow fn
eq nonce, fn()
#### Try/Catch/Finally Interaction With Other Constructs
test "try/catch with empty catch as last statement in a function body", ->
fn = ->
try nonce
catch err
eq nonce, fn()

View File

@@ -0,0 +1,96 @@
# Helpers
# -------
# pull the helpers from `CoffeeScript.helpers` into local variables
{starts, ends, compact, count, merge, extend, flatten, del, last} = CoffeeScript.helpers
#### `starts`
test "the `starts` helper tests if a string starts with another string", ->
ok starts('01234', '012')
ok not starts('01234', '123')
test "the `starts` helper can take an optional offset", ->
ok starts('01234', '34', 3)
ok not starts('01234', '01', 1)
#### `ends`
test "the `ends` helper tests if a string ends with another string", ->
ok ends('01234', '234')
ok not ends('01234', '012')
test "the `ends` helper can take an optional offset", ->
ok ends('01234', '012', 2)
ok not ends('01234', '234', 6)
#### `compact`
test "the `compact` helper removes falsey values from an array, preserves truthy ones", ->
allValues = [1, 0, false, obj={}, [], '', ' ', -1, null, undefined, true]
truthyValues = [1, obj, [], ' ', -1, true]
arrayEq truthyValues, compact(allValues)
#### `count`
test "the `count` helper counts the number of occurances of a string in another string", ->
eq 1/0, count('abc', '')
eq 0, count('abc', 'z')
eq 1, count('abc', 'a')
eq 1, count('abc', 'b')
eq 2, count('abcdc', 'c')
eq 2, count('abcdabcd','abc')
#### `merge`
test "the `merge` helper makes a new object with all properties of the objects given as its arguments", ->
ary = [0, 1, 2, 3, 4]
obj = {}
merged = merge obj, ary
ok merged isnt obj
ok merged isnt ary
for own key, val of ary
eq val, merged[key]
#### `extend`
test "the `extend` helper performs a shallow copy", ->
ary = [0, 1, 2, 3]
obj = {}
# should return the object being extended
eq obj, extend(obj, ary)
# should copy the other object's properties as well (obviously)
eq 2, obj[2]
#### `flatten`
test "the `flatten` helper flattens an array", ->
success = yes
(success and= typeof n is 'number') for n in flatten [0, [[[1]], 2], 3, [4]]
ok success
#### `del`
test "the `del` helper deletes a property from an object and returns the deleted value", ->
obj = [0, 1, 2]
eq 1, del(obj, 1)
ok 1 not of obj
#### `last`
test "the `last` helper returns the last item of an array-like object", ->
ary = [0, 1, 2, 3, 4]
eq 4, last(ary)
test "the `last` helper allows one to specify an optional offset", ->
ary = [0, 1, 2, 3, 4]
eq 2, last(ary, 2)

View File

@@ -0,0 +1,18 @@
# Importing
# ---------
unless window? or testingBrowser?
test "coffeescript modules can be imported and executed", ->
magicKey = __filename
magicValue = 0xFFFF
if global[magicKey]?
if exports?
local = magicValue
exports.method = -> local
else
global[magicKey] = {}
if require?.extensions? or require?.registerExtension?
ok require(__filename).method() is magicValue
delete global[magicKey]

View File

@@ -0,0 +1,121 @@
# Interpolation
# -------------
#### String Interpolation
# TODO: refactor string interpolation tests
eq 'multiline nested "interpolations" work', """multiline #{
"nested #{
ok true
"\"interpolations\""
}"
} work"""
# Issue #923: Tricky interpolation.
eq "#{ "{" }", "{"
eq "#{ '#{}}' } }", '#{}} }'
eq "#{"'#{ ({a: "b#{1}"}['a']) }'"}", "'b1'"
hello = 'Hello'
world = 'World'
ok '#{hello} #{world}!' is '#{hello} #{world}!'
ok "#{hello} #{world}!" is 'Hello World!'
ok "[#{hello}#{world}]" is '[HelloWorld]'
ok "#{hello}##{world}" is 'Hello#World'
ok "Hello #{ 1 + 2 } World" is 'Hello 3 World'
ok "#{hello} #{ 1 + 2 } #{world}" is "Hello 3 World"
[s, t, r, i, n, g] = ['s', 't', 'r', 'i', 'n', 'g']
ok "#{s}#{t}#{r}#{i}#{n}#{g}" is 'string'
ok "\#{s}\#{t}\#{r}\#{i}\#{n}\#{g}" is '#{s}#{t}#{r}#{i}#{n}#{g}'
ok "\#{string}" is '#{string}'
ok "\#{Escaping} first" is '#{Escaping} first'
ok "Escaping \#{in} middle" is 'Escaping #{in} middle'
ok "Escaping \#{last}" is 'Escaping #{last}'
ok "##" is '##'
ok "#{}" is ''
ok "#{}A#{} #{} #{}B#{}" is 'A B'
ok "\\\#{}" is '\\#{}'
ok "I won ##{20} last night." is 'I won #20 last night.'
ok "I won ##{'#20'} last night." is 'I won ##20 last night.'
ok "#{hello + world}" is 'HelloWorld'
ok "#{hello + ' ' + world + '!'}" is 'Hello World!'
list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
ok "values: #{list.join(', ')}, length: #{list.length}." is 'values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, length: 10.'
ok "values: #{list.join ' '}" is 'values: 0 1 2 3 4 5 6 7 8 9'
obj = {
name: 'Joe'
hi: -> "Hello #{@name}."
cya: -> "Hello #{@name}.".replace('Hello','Goodbye')
}
ok obj.hi() is "Hello Joe."
ok obj.cya() is "Goodbye Joe."
ok "With #{"quotes"}" is 'With quotes'
ok 'With #{"quotes"}' is 'With #{"quotes"}'
ok "Where is #{obj["name"] + '?'}" is 'Where is Joe?'
ok "Where is #{"the nested #{obj["name"]}"}?" is 'Where is the nested Joe?'
ok "Hello #{world ? "#{hello}"}" is 'Hello World'
ok "Hello #{"#{"#{obj["name"]}" + '!'}"}" is 'Hello Joe!'
a = """
Hello #{ "Joe" }
"""
ok a is "Hello Joe"
a = 1
b = 2
c = 3
ok "#{a}#{b}#{c}" is '123'
result = null
stash = (str) -> result = str
stash "a #{ ('aa').replace /a/g, 'b' } c"
ok result is 'a bb c'
foo = "hello"
ok "#{foo.replace("\"", "")}" is 'hello'
val = 10
a = """
basic heredoc #{val}
on two lines
"""
b = '''
basic heredoc #{val}
on two lines
'''
ok a is "basic heredoc 10\non two lines"
ok b is "basic heredoc \#{val}\non two lines"
eq 'multiline nested "interpolations" work', """multiline #{
"nested #{(->
ok yes
"\"interpolations\""
)()}"
} work"""
#### Regular Expression Interpolation
# TODO: improve heregex interpolation tests
test "heregex interpolation", ->
eq /\\#{}\\\"/ + '', ///
#{
"#{ '\\' }" # normal comment
}
# regex comment
\#{}
\\ \"
/// + ''

View File

@@ -0,0 +1,225 @@
# Operators
# ---------
test "binary (2-ary) math operators do not require spaces", ->
a = 1
b = -1
eq +1, a*-b
eq -1, a*+b
eq +1, a/-b
eq -1, a/+b
test "operators should respect new lines as spaced", ->
a = 123 +
456
eq 579, a
b = "1#{2}3" +
"456"
eq '123456', b
test "multiple operators should space themselves", ->
eq (+ +1), (- -1)
test "bitwise operators", ->
eq 2, (10 & 3)
eq 11, (10 | 3)
eq 9, (10 ^ 3)
eq 80, (10 << 3)
eq 1, (10 >> 3)
eq 1, (10 >>> 3)
num = 10; eq 2, (num &= 3)
num = 10; eq 11, (num |= 3)
num = 10; eq 9, (num ^= 3)
num = 10; eq 80, (num <<= 3)
num = 10; eq 1, (num >>= 3)
num = 10; eq 1, (num >>>= 3)
test "`instanceof`", ->
ok new String instanceof String
ok new Boolean instanceof Boolean
# `instanceof` supports negation by prefixing the operator with `not`
ok new Number not instanceof String
ok new Array not instanceof Boolean
#### Compound Assignment Operators
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
#### `is`,`isnt`,`==`,`!=`
test "`==` and `is` should be interchangeable", ->
a = b = 1
ok a is 1 and b == 1
ok a == b
ok a is b
test "`!=` and `isnt` should be interchangeable", ->
a = 0
b = 1
ok a isnt 1 and b != 0
ok a != b
ok a isnt b
#### `in`, `of`
# - `in` should check if an array contains a value using `indexOf`
# - `of` should check if a property is defined on an object using `in`
test "in, of", ->
arr = [1]
ok 0 of arr
ok 1 in arr
# prefixing `not` to `in and `of` should negate them
ok 1 not of arr
ok 0 not in arr
test "`in` should be able to operate on an array literal", ->
ok 2 in [0, 1, 2, 3]
ok 4 not in [0, 1, 2, 3]
arr = [0, 1, 2, 3]
ok 2 in arr
ok 4 not in arr
# should cache the value used to test the array
arr = [0]
val = 0
ok val++ in arr
ok val++ not in arr
val = 0
ok val++ of arr
ok val++ not of arr
test "`of` and `in` should be able to operate on instance variables", ->
obj = {
list: [2,3]
in_list: (value) -> value in @list
not_in_list: (value) -> value not in @list
of_list: (value) -> value of @list
not_of_list: (value) -> value not of @list
}
ok obj.in_list 3
ok obj.not_in_list 1
ok obj.of_list 0
ok obj.not_of_list 2
test "#???: `in` with cache and `__indexOf` should work in argument lists", ->
eq 1, [Object() in Array()].length
test "#737: `in` should have higher precedence than logical operators", ->
eq 1, 1 in [1] and 1
test "#768: `in` should preserve evaluation order", ->
share = 0
a = -> share++ if share is 0
b = -> share++ if share is 1
c = -> share++ if share is 2
ok a() not in [b(),c()]
eq 3, share
#### Chainable Operators
test "chainable operators", ->
ok 100 > 10 > 1 > 0 > -1
ok -1 < 0 < 1 < 10 < 100
test "`is` and `isnt` may be chained", ->
ok true is not false is true is not false
ok 0 is 0 isnt 1 is 1
test "different comparison operators (`>`,`<`,`is`,etc.) may be combined", ->
ok 1 < 2 > 1
ok 10 < 20 > 2+3 is 5
test "some chainable operators can be negated by `unless`", ->
ok (true unless 0==10!=100)
test "operator precedence: `|` lower than `<`", ->
eq 1, 1 | 2 < 3 < 4
test "preserve references", ->
a = b = c = 1
# `a == b <= c` should become `a === b && b <= c`
# (this test does not seem to test for this)
ok a == b <= c
test "chained operations should evaluate each value only once", ->
a = 0
ok 1 > a++ < 1
test "#891: incorrect inversion of chained comparisons", ->
ok (true unless 0 > 1 > 2)
ok (true unless (NaN = 0/0) < 0/0 < NaN)

View File

@@ -0,0 +1,32 @@
# Option Parser
# -------------
# TODO: refactor option parser tests
# Ensure that the OptionParser handles arguments correctly.
return unless require?
{OptionParser} = require './../lib/optparse'
opt = new OptionParser [
['-r', '--required [DIR]', 'desc required']
['-o', '--optional', 'desc optional']
['-l', '--list [FILES*]', 'desc list']
]
result = opt.parse ['one', 'two', 'three', '-r', 'dir']
ok result.arguments.length is 5
ok result.arguments[3] is '-r'
result = opt.parse ['--optional', '-r', 'folder', 'one', 'two']
ok result.optional is true
ok result.required is 'folder'
ok result.arguments.join(' ') is 'one two'
result = opt.parse ['-l', 'one.txt', '-l', 'two.txt', 'three']
ok result.list instanceof Array
ok result.list.join(' ') is 'one.txt two.txt'
ok result.arguments.join(' ') is 'three'

View File

@@ -0,0 +1,74 @@
# Range Literals
# --------------
# shared array
shared = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
test "basic inclusive ranges", ->
arrayEq [1, 2, 3] , [1..3]
arrayEq [0, 1, 2] , [0..2]
arrayEq [0, 1] , [0..1]
arrayEq [0] , [0..0]
arrayEq [-1] , [-1..-1]
arrayEq [-1, 0] , [-1..0]
arrayEq [-1, 0, 1], [-1..1]
test "basic exclusive ranges", ->
arrayEq [1, 2, 3] , [1...4]
arrayEq [0, 1, 2] , [0...3]
arrayEq [0, 1] , [0...2]
arrayEq [0] , [0...1]
arrayEq [-1] , [-1...0]
arrayEq [-1, 0] , [-1...1]
arrayEq [-1, 0, 1], [-1...2]
arrayEq [], [1...1]
arrayEq [], [0...0]
arrayEq [], [-1...-1]
test "downward ranges", ->
arrayEq shared, [9..0].reverse()
arrayEq [5, 4, 3, 2] , [5..2]
arrayEq [2, 1, 0, -1], [2..-1]
arrayEq [3, 2, 1] , [3..1]
arrayEq [2, 1, 0] , [2..0]
arrayEq [1, 0] , [1..0]
arrayEq [0] , [0..0]
arrayEq [-1] , [-1..-1]
arrayEq [0, -1] , [0..-1]
arrayEq [1, 0, -1] , [1..-1]
arrayEq [0, -1, -2], [0..-2]
arrayEq [4, 3, 2], [4...1]
arrayEq [3, 2, 1], [3...0]
arrayEq [2, 1] , [2...0]
arrayEq [1] , [1...0]
arrayEq [] , [0...0]
arrayEq [] , [-1...-1]
arrayEq [0] , [0...-1]
arrayEq [0, -1] , [0...-2]
arrayEq [1, 0] , [1...-1]
arrayEq [2, 1, 0], [2...-1]
test "ranges with variables as enpoints", ->
[a, b] = [1, 3]
arrayEq [1, 2, 3], [a..b]
arrayEq [1, 2] , [a...b]
b = -2
arrayEq [1, 0, -1, -2], [a..b]
arrayEq [1, 0, -1] , [a...b]
test "ranges with expressions as endpoints", ->
[a, b] = [1, 3]
arrayEq [2, 3, 4, 5, 6], [(a+1)..2*b]
arrayEq [2, 3, 4, 5] , [(a+1)...2*b]
test "large ranges are generated with looping constructs", ->
down = [99..0]
eq 100, (len = down.length)
eq 0, down[len - 1]
up = [0...100]
eq 100, (len = up.length)
eq 99, up[len - 1]

View File

@@ -0,0 +1,45 @@
# Regular Expression Literals
# ---------------------------
test "basic regular expression literals", ->
ok 'a'.match(/a/)
ok 'a'.match /a/
ok 'a'.match(/a/g)
ok 'a'.match /a/g
test "division is not confused for a regular expression", ->
eq 2, 4 / 2 / 1
a = 4
b = 2
g = 1
eq 2, a / b/g
obj = method: -> 2
two = 2
eq 2, (obj.method()/two + obj.method()/two)
i = 1
eq 2, (4)/2/i
eq 1, i/i/i
test "backslash escapes", ->
eq "\\/\\\\", /\/\\/.source
test "#764: regular expressions should be indexable", ->
eq /0/['source'], ///#{0}///['source']
test "#584: slashes are allowed unescaped in character classes", ->
ok /^a\/[/]b$/.test 'a//b'
#### Heregexe(n|s)
test "a heregex will ignore whitespace and comments", ->
eq /^I'm\x20+[a]\s+Heregex?\/\/\//gim + '', ///
^ I'm \x20+ [a] \s+
Heregex? / // # or not
///gim + ''
test "an empty heregex will compile to an empty, non-capturing group", ->
eq /(?:)/ + '', /// /// + ''

View File

@@ -1,83 +1,10 @@
# Ranges, Slices, and Splices
# ---------------------------
# Slicing and Splicing
# --------------------
# shared array
shared = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
#### Ranges
test "basic inclusive ranges", ->
arrayEq [1, 2, 3] , [1..3]
arrayEq [0, 1, 2] , [0..2]
arrayEq [0, 1] , [0..1]
arrayEq [0] , [0..0]
arrayEq [-1] , [-1..-1]
arrayEq [-1, 0] , [-1..0]
arrayEq [-1, 0, 1], [-1..1]
test "basic exclusive ranges", ->
arrayEq [1, 2, 3] , [1...4]
arrayEq [0, 1, 2] , [0...3]
arrayEq [0, 1] , [0...2]
arrayEq [0] , [0...1]
arrayEq [-1] , [-1...0]
arrayEq [-1, 0] , [-1...1]
arrayEq [-1, 0, 1], [-1...2]
arrayEq [], [1...1]
arrayEq [], [0...0]
arrayEq [], [-1...-1]
test "downward ranges", ->
arrayEq shared, [9..0].reverse()
arrayEq [5, 4, 3, 2] , [5..2]
arrayEq [2, 1, 0, -1], [2..-1]
arrayEq [3, 2, 1] , [3..1]
arrayEq [2, 1, 0] , [2..0]
arrayEq [1, 0] , [1..0]
arrayEq [0] , [0..0]
arrayEq [-1] , [-1..-1]
arrayEq [0, -1] , [0..-1]
arrayEq [1, 0, -1] , [1..-1]
arrayEq [0, -1, -2], [0..-2]
arrayEq [4, 3, 2], [4...1]
arrayEq [3, 2, 1], [3...0]
arrayEq [2, 1] , [2...0]
arrayEq [1] , [1...0]
arrayEq [] , [0...0]
arrayEq [] , [-1...-1]
arrayEq [0] , [0...-1]
arrayEq [0, -1] , [0...-2]
arrayEq [1, 0] , [1...-1]
arrayEq [2, 1, 0], [2...-1]
test "ranges with variables as enpoints", ->
[a, b] = [1, 3]
arrayEq [1, 2, 3], [a..b]
arrayEq [1, 2] , [a...b]
b = -2
arrayEq [1, 0, -1, -2], [a..b]
arrayEq [1, 0, -1] , [a...b]
test "ranges with expressions as endpoints", ->
[a, b] = [1, 3]
arrayEq [2, 3, 4, 5, 6], [(a+1)..2*b]
arrayEq [2, 3, 4, 5] , [(a+1)...2*b]
test "large ranges are generated with looping constructs", ->
down = [99..0]
eq 100, (len = down.length)
eq 0, down[len - 1]
up = [0...100]
eq 100, (len = up.length)
eq 99, up[len - 1]
#### Slices
#### Slicing
test "basic slicing", ->
arrayEq [7, 8, 9] , shared[7..9]
@@ -123,7 +50,7 @@ test "string slicing", ->
ok str[-5..] is "vwxyz"
#### Splices
#### Splicing
test "basic splicing", ->
ary = [0..9]

View File

@@ -0,0 +1,96 @@
# String Literals
# ---------------
# TODO: refactor string literal tests
eq '(((dollars)))', '\(\(\(dollars\)\)\)'
eq 'one two three', "one
two
three"
eq "four five", 'four
five'
#647
eq "''Hello, World\\''", '''
'\'Hello, World\\\''
'''
eq '""Hello, World\\""', """
"\"Hello, World\\\""
"""
eq 'Hello, World\n', '''
Hello, World\
'''
a = """
basic heredoc
on two lines
"""
ok a is "basic heredoc\non two lines"
a = '''
a
"b
c
'''
ok a is "a\n \"b\nc"
a = """
a
b
c
"""
ok a is "a\n b\n c"
a = '''one-liner'''
ok a is 'one-liner'
a = """
out
here
"""
ok a is "out\nhere"
a = '''
a
b
c
'''
ok a is " a\n b\nc"
a = '''
a
b c
'''
ok a is "a\n\n\nb c"
a = '''more"than"one"quote'''
ok a is 'more"than"one"quote'
a = '''here's an apostrophe'''
ok a is "here's an apostrophe"
# The indentation detector ignores blank lines without trailing whitespace
a = """
one
two
"""
ok a is "one\ntwo\n"
eq ''' line 0
should not be relevant
to the indent level
''', '
line 0\n
should not be relevant\n
to the indent level
'
eq ''' '\\\' ''', " '\\' "
eq """ "\\\" """, ' "\\" '
eq ''' <- keep these spaces -> ''', ' <- keep these spaces -> '