-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathtest.coffee
135 lines (118 loc) · 5.39 KB
/
test.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
{transformAst, createVariableName} = require './index'
{parse, print} = require 'recast'
{spy} = require 'sinon'
assert = require 'assert'
toAst = (source) -> parse(source).program
toSource = (ast) -> print(ast).code
describe 'AST transformation:', ->
# Needs to be on the test object, since mocha-given tries to eval().call(this)
# when tests fail. `toSource` is not in scope in these cases
Given -> @toSource = toSource
Given -> @varName = 'arbitrary'
Given -> @createName = => @varName
describe 'passes an array of all encountered variable names to the `createName` callback:', ->
Given -> @ast = toAst('''
var variable = 1;
function aFunction(foo, bar) {
var baz = 1, anotherFunction = function captureThis() {
var andCaptureThis;
};
function innerFunction(a, b) {}
return innerFunction(baz, anotherFunction);
}
var anotherVariable = aFunction(variable, function(error, data) {
if (error) {
var invalidData = data;
}
});
var bar = window;''')
Given -> @createName = spy(-> 'arbitrary')
When -> transformAst(@ast, @createName)
Then -> assert.deepEqual @createName.firstCall.args[0].sort(), [
'a', 'aFunction', 'andCaptureThis', 'anotherFunction', 'anotherVariable',
'b', 'bar', 'baz', 'captureThis', 'data', 'error', 'foo', 'innerFunction',
'invalidData', 'variable', 'window',
]
describe 'placement of the string array:', ->
describe 'wraps code in an IIFE, using the map variable name provided by `createName` and the array as parameter and arguments:', ->
Given -> @source = '1 + 1'
Given -> @expected = """
(function(#{@varName}) {
#{@source};
}).call(this, [])
"""
When -> @obfuscated = transformAst(toAst(@source), @createName)
Then -> @toSource(@obfuscated) == @expected
describe 'Places the string map at the begin (no IIFE) if any variable declarations exist at the top level:', ->
Given -> @source = 'while (true) { var foo = 1; }';
Given -> @expected = "var #{@varName} = [];\n#{@source}"
When -> @obfuscated = transformAst(toAst(@source), @createName)
Then -> @toSource(@obfuscated) == @expected
describe 'Places the string map at the begin (no IIFE) if any function declarations exist at the top level:', ->
Given -> @source = 'function one() { return 1; }';
Given -> @expected = "var #{@varName} = [];\n#{@source}"
When -> @obfuscated = transformAst(toAst(@source), @createName)
Then -> @toSource(@obfuscated) == @expected
describe 'replacement of strings:', ->
Given -> @source = '''call(['a string', true, false, null, 1.2, -3, 'another string']);'''
Given -> @expected = """
(function(#{@varName}) {
call([#{@varName}[0], true, false, null, 1.2, -3, #{@varName}[1]]);
}).call(this, ["a string", "another string"]);
"""
When -> @obfuscated = transformAst(toAst(@source), @createName)
Then -> @toSource(@obfuscated) == @expected
describe 'replacement of property accesses:', ->
Given -> @source = 'object.method(some.value, another.property);';
Given -> @expected = """
(function(#{@varName}) {
object[#{@varName}[0]](some[#{@varName}[1]], another[#{@varName}[2]]);
}).call(this, ["method", "value", "property"]);
"""
When -> @obfuscated = transformAst(toAst(@source), @createName)
Then -> @toSource(@obfuscated) == @expected
describe 'leaves string properties in object literals intact:', ->
Given -> @source = 'call({"a string": 123});';
Given -> @expected = """
(function(#{@varName}) {
#{@source}
}).call(this, []);
"""
When -> @obfuscated = transformAst(toAst(@source), @createName)
Then -> @toSource(@obfuscated) == @expected
describe 'deduplication of encountered strings:', ->
Given -> @source = 'object.arbitrary = "arbitrary" + util.arbitrary("arbitrary")';
Given -> @expected = """
(function(#{@varName}) {
object[#{@varName}[0]] = #{@varName}[0] + util[#{@varName}[0]](#{@varName}[0]);
}).call(this, ["arbitrary"])
"""
When -> @obfuscated = transformAst(toAst(@source), @createName)
Then -> @toSource(@obfuscated) == @expected
describe 'leaves "use strict" string literals intact:', ->
Given -> @source = '"use strict";\n call({"a string": 123});'
Given -> @expected = """
(function(#{@varName}) {
#{@source}
}).call(this, []);
"""
When -> @obfuscated = transformAst(toAst(@source), @createName)
Then -> @toSource(@obfuscated) == @expected
describe 'does not place the string map before a leading "use strict":', ->
Given -> @source = '"use strict"; var a;'
Given -> @expected = """
"use strict";
var #{@varName} = [];
var a;
"""
When -> @obfuscated = transformAst(toAst(@source), @createName)
Then -> @toSource(@obfuscated) == @expected
describe 'variable name creator:', ->
describe 'it creates a variable name that maches /^_x\\d+$/:', ->
Given -> @varNames = [];
When -> @created = createVariableName(@varNames)
Then -> /_x\d+$/.test(@created)
describe 'it does not return a passed-in variable name:', ->
Given -> @varNames = ["_x#{i}" for i in [0..0xffff]]
When -> @created = createVariableName(@varNames)
Then -> @varNames.indexOf(@created) == -1