react / wstein / node_modules / react / node_modules / envify / node_modules / jstransform / src / __tests__ / jstransform-test.js
80551 views/**1* Copyright 2013 Facebook, Inc.2*3* Licensed under the Apache License, Version 2.0 (the "License");4* you may not use this file except in compliance with the License.5* You may obtain a copy of the License at6*7* http://www.apache.org/licenses/LICENSE-2.08*9* Unless required by applicable law or agreed to in writing, software10* distributed under the License is distributed on an "AS IS" BASIS,11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12* See the License for the specific language governing permissions and13* limitations under the License.14*15* @emails [email protected]16*/1718require('mock-modules').autoMockOff();1920describe('jstransform', function() {21var transformFn;22var Syntax = require('esprima-fb').Syntax;2324beforeEach(function() {25require('mock-modules').dumpCache();26transformFn = require('../jstransform').transform;27});2829function _runVisitor(source, nodeCount, visitor) {30var actualVisitationCount = 0;31function shimVisitor(traverse, node, path, state) {32actualVisitationCount++;33return visitor(traverse, node, path, state);34}35shimVisitor.test = visitor.test;36transformFn([shimVisitor], source);37expect(actualVisitationCount).toBe(nodeCount);38}3940function testScopeBoundary(source, localIdents, nodeCount, visitorTest) {41function visitor(traverse, node, path, state) {42var actualLocalIdents = Object.keys(state.localScope.identifiers);43expect(actualLocalIdents.sort()).toEqual(localIdents.sort());44}45visitor.test = visitorTest;46_runVisitor(source, nodeCount, visitor);47}4849function testParentScope(source, parentIdents, nodeCount, visitorTest) {50function visitor(traverse, node, path, state) {51parentIdents = parentIdents && parentIdents.sort();52var parentScope = state.localScope.parentScope;53var actualParentIdents =54parentScope && Object.keys(parentScope.identifiers).sort();55expect(actualParentIdents).toEqual(parentIdents);56}57visitor.test = visitorTest;58_runVisitor(source, nodeCount, visitor);59}6061describe('closure scope boundaries', function() {62it('creates a scope boundary around Program scope', function() {63var source =64'var foo;' +65'var bar, baz;' +66'function blah() {}';67var idents = ['foo', 'bar', 'baz', 'blah'];6869testScopeBoundary(source, idents, 3, function(node, path) {70return path[0] && path[0].type === Syntax.Program;71});72});7374it('creates a scope boundary around FunctionDeclarations', function() {75var source =76'var foo;' +77'function blah() {' +78' var bar;' +79' function nested() {' +80' var baz;' +81' }' +82'}';83var programIdents = ['foo', 'blah'];84var blahIdents = ['arguments', 'bar', 'nested'];85var nestedIdents = ['arguments', 'baz'];8687testScopeBoundary(source, programIdents, 2, function(node, path) {88return path[0] && path[0].type === Syntax.Program;89});9091testScopeBoundary(source, blahIdents, 2, function(node, path) {92// All direct children of blah()93return path[0] && path[0].type === Syntax.BlockStatement &&94path[1] && path[1].type === Syntax.FunctionDeclaration &&95path[1].id.name === 'blah';96});9798testScopeBoundary(source, nestedIdents, 1, function(node, path) {99// All direct children of nested()100return path[0] && path[0].type === Syntax.BlockStatement &&101path[1] && path[1].type === Syntax.FunctionDeclaration &&102path[1].id.name === 'nested';103});104});105106it('creates a scope boundary around MethodDefinitions', function() {107var source =108'var foo;' +109'class ClassA {' +110' blah() {' +111' var bar;' +112' }' +113' another() {' +114' var baz;' +115' }' +116'}';117var programIdents = ['foo', 'ClassA'];118var blahIdents = ['arguments', 'bar'];119var anotherIdents = ['arguments', 'baz'];120121testScopeBoundary(source, programIdents, 2, function(node, path) {122return path[0] && path[0].type === Syntax.Program;123});124125testScopeBoundary(source, blahIdents, 1, function(node, path) {126// All direct children of blah()127return path[0] && path[0].type === Syntax.BlockStatement &&128path[1] && path[1].type === Syntax.FunctionExpression &&129path[2] && path[2].type === Syntax.MethodDefinition &&130path[2].key.name === 'blah';131});132133testScopeBoundary(source, anotherIdents, 1, function(node, path) {134// All direct children of another()135return path[0] && path[0].type === Syntax.BlockStatement &&136path[1] && path[1].type === Syntax.FunctionExpression &&137path[2] && path[2].type === Syntax.MethodDefinition &&138path[2].key.name === 'another';139});140});141142it('creates a scope boundary around concise ArrowFunc exprs', function() {143var source =144'var foo;' +145'var bar = baz => baz;';146147var programIdents = ['foo', 'bar'];148var barIdents = ['arguments', 'baz'];149150testScopeBoundary(source, programIdents, 2, function(node, path) {151return path[0] && path[0].type === Syntax.Program;152});153154testScopeBoundary(source, barIdents, 1, function(node, path) {155return path[0] && path[0].type === Syntax.ArrowFunctionExpression156&& path[0].body === node;157});158});159160it('uses VariableDeclarations to determine scope boundary', function() {161var source =162'var foo = 1;' +163'function bar() {' +164' foo++;' +165' function baz() {' +166' var foo = 2;' +167' }' +168'}';169var programIdents = ['foo', 'bar'];170var barIdents = ['arguments', 'baz'];171var bazIdents = ['arguments', 'foo'];172173testScopeBoundary(source, programIdents, 2, function(node, path) {174return path[0] && path[0].type === Syntax.Program;175});176177testScopeBoundary(source, barIdents, 2, function(node, path) {178// All direct children of blah()179return path[0] && path[0].type === Syntax.BlockStatement &&180path[1] && path[1].type === Syntax.FunctionDeclaration &&181path[1].id.name === 'bar';182});183184testScopeBoundary(source, bazIdents, 1, function(node, path) {185// All direct children of baz()186return path[0] && path[0].type === Syntax.BlockStatement &&187path[1] && path[1].type === Syntax.FunctionDeclaration &&188path[1].id.name === 'baz';189});190});191192it('includes function args in functions scope boundary', function() {193var source =194'var foo;' +195'function blah(bar) {' +196' var baz;' +197'}' +198'var blah2 = bar2 => {var baz;};' +199'var blah3 = bar3 => bar3;';200var programIdents = ['foo', 'blah', 'blah2', 'blah3'];201var blahIdents = ['arguments', 'bar', 'baz'];202var blah2Idents = ['arguments', 'bar2', 'baz'];203var blah3Idents = ['arguments', 'bar3'];204205testScopeBoundary(source, programIdents, 4, function(node, path) {206return path[0] && path[0].type === Syntax.Program;207});208209testScopeBoundary(source, blahIdents, 1, function(node, path) {210// All direct children of blah()211return path[0] && path[0].type === Syntax.BlockStatement &&212path[1] && path[1].type === Syntax.FunctionDeclaration &&213path[1].id.name === 'blah';214});215216testScopeBoundary(source, blah2Idents, 1, function(node, path) {217// All direct children of blah2()218return path[0] && path[0].type === Syntax.BlockStatement &&219path[1] && path[1].type === Syntax.ArrowFunctionExpression &&220path[2].id.name === 'blah2';221});222223testScopeBoundary(source, blah3Idents, 1, function(node, path) {224// All direct children of blah3()225return path[0] && path[0].type === Syntax.ArrowFunctionExpression &&226path[0].body === node &&227path[1].id.name === 'blah3';228});229});230231it('includes rest param args in function scope boundaries', function() {232var source =233'var foo;' +234'function blah(...bar) {' +235' var baz;' +236'}' +237'var blah2 = (...bar2) => {var baz;};' +238'var blah3 = (...bar3) => bar3;';239var programIdents = ['foo', 'blah', 'blah2', 'blah3'];240var blahIdents = ['arguments', 'bar', 'baz'];241var blah2Idents = ['arguments', 'bar2', 'baz'];242var blah3Idents = ['arguments', 'bar3'];243244testScopeBoundary(source, programIdents, 4, function(node, path) {245return path[0] && path[0].type === Syntax.Program;246});247248testScopeBoundary(source, blahIdents, 1, function(node, path) {249// All direct children of blah()250return path[0] && path[0].type === Syntax.BlockStatement &&251path[1] && path[1].type === Syntax.FunctionDeclaration &&252path[1].id.name === 'blah';253});254255testScopeBoundary(source, blah2Idents, 1, function(node, path) {256// All direct children of blah2()257return path[0] && path[0].type === Syntax.BlockStatement &&258path[1] && path[1].type === Syntax.ArrowFunctionExpression &&259path[2].id.name === 'blah2';260});261262testScopeBoundary(source, blah3Idents, 1, function(node, path) {263// All direct children of blah3()264return path[0] && path[0].type === Syntax.ArrowFunctionExpression &&265path[0].body === node &&266path[1].id.name === 'blah3';267});268});269270it('puts FunctionExpression names within function scope', function() {271var source =272'var foo;' +273'var bar = function baz() {' +274' var blah;' +275'};';276var programIdents = ['foo', 'bar'];277var bazIdents = ['arguments', 'baz', 'blah'];278279testScopeBoundary(source, programIdents, 2, function(node, path) {280return path[0] && path[0].type === Syntax.Program;281});282283testScopeBoundary(source, bazIdents, 1, function(node, path) {284// All direct children of baz()285return path[0] && path[0].type === Syntax.BlockStatement &&286path[1] && path[1].type === Syntax.FunctionExpression &&287path[1].id.name === 'baz';288});289});290});291292describe('block scope boundaries', function() {293it('creates a scope boundary around CatchClauses with params', function() {294var source =295'var blah = 0;' +296'try {' +297'} catch (e) {' +298' blah++;' +299'}';300var programIdents = ['blah'];301var catchIdents = ['e'];302303testScopeBoundary(source, programIdents, 2, function(node, path) {304return path[0] && path[0].type === Syntax.Program;305});306307testScopeBoundary(source, catchIdents, 1, function(node, path) {308// All direct children of catch(e) block309return path[0] && path[0].type === Syntax.BlockStatement &&310path[1] && path[1].type === Syntax.CatchClause;311});312});313314it('includes vars defined in CatchClauses in the parent scope', function() {315var source =316'try {' +317'} catch (e) {' +318' var blah;' +319'}';320var programIdents = ['blah'];321var catchIdents = ['e'];322323testScopeBoundary(source, programIdents, 1, function(node, path) {324return path[0] && path[0].type === Syntax.Program;325});326327testScopeBoundary(source, catchIdents, 1, function(node, path) {328// All direct children of catch(e) block329return path[0] && path[0].type === Syntax.BlockStatement &&330path[1] && path[1].type === Syntax.CatchClause;331});332});333});334335describe('scope chain linking', function() {336it('links parent scope boundaries', function() {337var source =338'var foo;' +339'function blah() {' +340' var bar;' +341' function nested() {' +342' var baz;' +343' }' +344'}';345var programIdents = ['foo', 'blah'];346var blahIdents = ['arguments', 'bar', 'nested'];347348testParentScope(source, programIdents, 2, function(node, path) {349// All direct children of blah()350return path[0] && path[0].type === Syntax.BlockStatement &&351path[1] && path[1].type === Syntax.FunctionDeclaration &&352path[1].id.name === 'blah';353});354355testParentScope(source, blahIdents, 1, function(node, path) {356// All direct children of nested()357return path[0] && path[0].type === Syntax.BlockStatement &&358path[1] && path[1].type === Syntax.FunctionDeclaration &&359path[1].id.name === 'nested';360});361});362363it('nests MethodDefinition boundaries under parent scope', function() {364var source =365'var foo;' +366'class ClassA {' +367' blah() {' +368' var bar;' +369' }' +370'}';371var programIdents = ['foo', 'ClassA'];372373testParentScope(source, programIdents, 1, function(node, path) {374// All direct children of blah()375return path[0] && path[0].type === Syntax.BlockStatement &&376path[1] && path[1].type === Syntax.FunctionExpression &&377path[2] && path[2].type === Syntax.MethodDefinition &&378path[2].key.name === 'blah';379});380});381});382383describe('"use strict" tracking', function() {384function testStrictness(expectedStrict, source) {385var visitedNodes = 0;386function visitor(traverse, node, path, state) {387visitedNodes++;388expect(state.scopeIsStrict).toBe(expectedStrict);389}390visitor.test = function(node, path, state) {391return node.type === Syntax.Literal392&& node.value === 'testStr';393};394transformFn([visitor], source);395expect(visitedNodes).toBe(1);396}397398it('detects program-level strictness', function() {399testStrictness(false, '"testStr";');400testStrictness(true, '"use strict"; "testStr";');401});402403it('detects non-inherited strictness', function() {404testStrictness(true, [405'function foo() {',406' "use strict";',407' "testStr";',408'}'409].join('\n'));410});411412it('detects program-inherited strictness', function() {413testStrictness(true, [414'"use strict";',415'function foo() {',416' "testStr";',417'}'418].join('\n'));419});420421it('detects function-inherited strictness', function() {422testStrictness(true, [423'function foo() {',424' "use strict";',425' function bar() {',426' "testStr";',427' }',428'}'429].join('\n'));430});431432it('does not detect sibling strictness', function() {433testStrictness(false, [434'function foo() {',435' "use strict";',436'}',437'function bar() {',438' "testStr";',439'}'440].join('\n'));441});442});443444describe('visitors', function() {445it('should visit nodes in order', function() {446var source = [447'// Foo comment',448'function foo() {}',449'',450'// Bar comment',451'function bar() {}'452].join('\n');453454var actualNodes = [];455456function visitFunction(traverse, node, path, state) {457actualNodes.push([node.id.name, node.range[0]]);458}459visitFunction.test = function(node, path, state) {460return node.type === Syntax.FunctionDeclaration;461};462463function visitComments(traverse, node, path, state) {464actualNodes.push([node.value, node.range[0]]);465}466visitComments.test = function(node, path, state) {467return node.type === 'Line';468};469470transformFn([visitComments, visitFunction], source);471472expect(actualNodes).toEqual([473[' Foo comment', 0],474['foo', 15],475[' Bar comment', 34],476['bar', 49]477]);478});479});480});481482483