├── README.md └── src ├── test.html ├── test_hosted.html └── tests.js /README.md: -------------------------------------------------------------------------------- 1 | ###Strict Mode Tester 2 | 3 | This is the source companion to my [blog post](http://javascriptweblog.wordpress.com/2011/05/03/javascript-strict-mode/) 4 | 5 | I've written a test for all 38 Strict Mode features as defined [here](http://ecma262-5.com/ELS5_HTML.htm#Annex_C) 6 | 7 | For the visual test page open `test.html` in your browser. (You can also run the hosted version [here](http://java-script.limewebs.com/strictMode/test_hosted.html)). 8 | 9 | The file `tests.js` can also be dumped into your developer console - just switch mode to CONSOLE_MODE 10 | -------------------------------------------------------------------------------- /src/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ECMA 5 Strict Mode Tests 7 | 8 | 9 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/test_hosted.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ECMA 5 Strict Mode Tests 8 | 9 | 10 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/tests.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | //////////////////////////////// 4 | //TEST UTILS... 5 | //////////////////////////////// 6 | 7 | var HTML_MODE = 0; 8 | var CONSOLE_MODE = 1; 9 | 10 | var POST_URL = "http://javascriptweblog.wordpress.com/2011/05/03/javascript-strict-mode/"; 11 | 12 | var mode = HTML_MODE; 13 | 14 | var banner = document.getElementById('banner'); 15 | var currentTestWidget; 16 | 17 | var testsPassed = 0; 18 | var totalTests = 0; 19 | 20 | window.console = window.console || {log:alert}; 21 | 22 | function testException(testName, code, expectedError) { 23 | 'use strict'; 24 | startTest(testName); 25 | try { 26 | expectedError == SyntaxError ? eval(code) : code(); 27 | finishTest(false); 28 | } catch (e) { 29 | (e instanceof expectedError) ? finishTest(true) : finishTest(false); 30 | } 31 | } 32 | 33 | function testValue(testName, fn, expectedValue, options) { 34 | 'use strict'; 35 | options = options || {}; 36 | startTest(testName); 37 | var result = (fn.apply(options.ctx, options.args || []) === expectedValue); 38 | finishTest(result); 39 | } 40 | 41 | function startTest(testName) { 42 | if (mode == CONSOLE_MODE) { 43 | console.log("testing..." + testName); 44 | } else { 45 | this.currentWidget = document.createElement('DIV'); 46 | this.currentWidget.innerHTML = testName; 47 | document.body.appendChild(this.currentWidget); 48 | } 49 | } 50 | 51 | function finishTest(passed) { 52 | totalTests++; 53 | passed && testsPassed++; 54 | var result = passed ? "passed" : "failed"; 55 | if (mode == CONSOLE_MODE) { 56 | console.log(result); 57 | } else { 58 | this.currentWidget.className = result; 59 | } 60 | } 61 | 62 | function startAll() { 63 | if (mode == HTML_MODE) { 64 | banner.innerHTML += [":", browser.browserName, browser.fullVersion].join(' '); 65 | } 66 | } 67 | 68 | function finishAll() { 69 | var result = ["","(", testsPassed, "out of", totalTests, "tests passed", ")"].join(' '); 70 | if (mode == HTML_MODE) { 71 | banner.innerHTML += result; 72 | var linkBack = document.createElement('a'); 73 | linkBack.href = POST_URL; 74 | linkBack.innerHTML = "what is strict mode?"; 75 | linkBack.target = "_blank"; 76 | banner.appendChild(linkBack); 77 | } else if (mode == CONSOLE_MODE) { 78 | console.log(result); 79 | } 80 | } 81 | 82 | //////////////////////////////////////////////////////////////////////////////////////////////// 83 | //THE TESTS (the following comments lifted directly from the ES5 spec http://es5.github.io/) 84 | //////////////////////////////////////////////////////////////////////////////////////////////// 85 | 86 | startAll(); 87 | 88 | // A conforming implementation, when processing strict mode code, may not extend the 89 | //syntax of NumericLiteral (7.8.3) to include OctalIntegerLiteral as described in B.1.1. 90 | testException("no octal literals", '012', SyntaxError); 91 | 92 | // A conforming implementation, when processing strict mode code (see 10.1.1), may not 93 | //extend the syntax of EscapeSequence to include OctalEscapeSequence as described in B.1.2. 94 | testException("no octal escape sequence", '"\\012"', SyntaxError); 95 | 96 | // Assignment to an undeclared identifier or otherwise unresolvable reference does not 97 | //create a property in the global object. When a simple assignment occurs within strict 98 | //mode code, its LeftHandSide must not evaluate to an unresolvable Reference. If it does 99 | //a ReferenceError exception is thrown (8.7.2). 100 | testException( 101 | "no implied globals", 102 | function () {'use strict'; x = 3;}, 103 | ReferenceError 104 | ); 105 | 106 | //The LeftHandSide also may not be a reference to a data property with the attribute 107 | //value {[[Writable]]:false}, to an accessor property with the attribute value 108 | //{[[Set]]:undefined}, nor to a non-existent property of an object whose [[Extensible]] 109 | //internal property has the value false. In these cases a TypeError exception is thrown 110 | //(11.13.1). 111 | var assignToNonWritable = function () { 112 | 'use strict'; 113 | var obj = {}; 114 | Object.defineProperty(obj, "name", { 115 | writable: false 116 | }); 117 | obj.name = "octopus"; 118 | } 119 | 120 | testException("can't assign to non-writable properties", assignToNonWritable, TypeError); 121 | 122 | var assignWhenSetterUndefined = function () { 123 | 'use strict'; 124 | var obj = {}; 125 | Object.defineProperty(obj, "name", { 126 | set: undefined 127 | }); 128 | obj.name = "cuttlefish"; 129 | } 130 | 131 | testException("can't assign when setter undefined", assignWhenSetterUndefined, TypeError); 132 | 133 | var assignToNonExtensible = function () { 134 | 'use strict'; 135 | var obj = {}; 136 | Object.preventExtensions(obj); 137 | obj.name = "jellyfish"; 138 | } 139 | 140 | testException("can't assign to non extensible", assignToNonExtensible, TypeError); 141 | 142 | //The identifier eval or arguments may not appear as the LeftHandSideExpression of an 143 | //Assignment operator (11.13) or of a PostfixExpression (11.13) or as the UnaryExpression 144 | //operated upon by a Prefix Increment (11.4.4) or a Prefix Decrement (11.4.5) operator. 145 | testException("can't assign to eval", "eval=3", SyntaxError); 146 | testException("can't assign to arguments", "arguments=3", SyntaxError); 147 | testException("can't postfix eval", "eval++", SyntaxError); 148 | testException("can't postfix arguments", "arguments++", SyntaxError); 149 | testException("can't prefix eval", "++eval", SyntaxError); 150 | testException("can't prefix arguments", "++arguments", SyntaxError); 151 | 152 | //Arguments objects for strict mode functions define non-configurable accessor properties 153 | //named "caller" and "callee" which throw a TypeError exception on access (10.6). 154 | testException( 155 | "can't use arguments.caller", 156 | function () {'use strict'; arguments.caller;}, 157 | TypeError 158 | ); 159 | 160 | testException( 161 | "can't use arguments.callee", 162 | function () {'use strict'; arguments.callee}, 163 | TypeError 164 | ); 165 | 166 | //Arguments objects for strict mode functions do not dynamically share their array indexed 167 | //property values with the corresponding formal parameter bindings of their functions. (10.6). 168 | var assignToArguments = function (x) { 169 | 'use strict'; 170 | arguments[0] = 3; 171 | return x; 172 | } 173 | 174 | testValue( 175 | "arguments not bound to formal params", 176 | assignToArguments, 177 | 5, 178 | {args: [5]} 179 | ); 180 | 181 | //For strict mode functions, if an arguments object is created the binding of the local 182 | //identifier arguments to the arguments object is immutable and hence may not be the 183 | //target of an assignment expression. (10.5). 184 | var assignToFormalParams = function (x) { 185 | 'use strict'; 186 | x = 3; 187 | return arguments[0]; 188 | } 189 | 190 | testValue( 191 | "arguments object is immutable", 192 | assignToFormalParams, 193 | 5, 194 | {args: [5]} 195 | ); 196 | 197 | //It is a SyntaxError if strict mode code contains an ObjectLiteral with more than one 198 | //definition of any data property (11.1.5). 199 | testException("no duplicate properties", "({a:1, a:2})", SyntaxError); 200 | 201 | //It is a SyntaxError if the Identifier "eval" or the Identifier "arguments occurs as the 202 | //Identifier in a PropertySetParameterList of a PropertyAssignment that is contained in 203 | //strict code or if its FunctionBody is strict code (11.1.5). 204 | testException( 205 | "eval not allowed in propertySetParameterList", 206 | "({set a(eval){ }})", 207 | SyntaxError 208 | ); 209 | 210 | testException( 211 | "arguments not allowed in propertySetParameterList", 212 | "({set a(arguments){ }})", 213 | SyntaxError 214 | ); 215 | 216 | //Strict mode eval code cannot instantiate variables or functions in the variable environment 217 | //of the caller to eval. Instead, a new variable environment is created and that environment 218 | //is used for declaration binding instantiation for the eval code (10.4.2). 219 | testException( 220 | "eval cannot create var in calling context", 221 | function () {'use strict'; eval('var a = 99'); a}, 222 | ReferenceError 223 | ); 224 | 225 | //If this is evaluated within strict mode code, then the this value is not coerced to an object. 226 | //A this value of null or undefined is not converted to the global object and primitive values 227 | //are not converted to wrapper objects. The this value passed via a function call (including 228 | //calls made using Function.prototype.apply and Function.prototype.call) do not coerce the 229 | //passed this value to an object (10.4.3, 11.1.1, 15.3.4.3, 15.3.4.4). 230 | var getThis = function () { 231 | 'use strict'; 232 | return this; 233 | } 234 | 235 | testValue( 236 | "this is not coerced", 237 | getThis, 238 | 4, 239 | {ctx: 4} 240 | ); 241 | 242 | testValue( 243 | "no global coercion for null", 244 | getThis, 245 | null, 246 | {ctx: null} 247 | ); 248 | 249 | 250 | //When a delete operator occurs within strict mode code, a SyntaxError is thrown if its 251 | //UnaryExpression is a direct reference to a variable, function argument, or function name 252 | //(11.4.1). 253 | testException("can't delete variable directly", "var a = 3; delete a", SyntaxError); 254 | testException("can't delete argument", "function(a) {delete a}", SyntaxError); 255 | testException("can't delete function by name", "function fn() {}; delete fn", SyntaxError); 256 | 257 | //When a delete operator occurs within strict mode code, a TypeError is thrown if the 258 | //property to be deleted has the attribute { [[Configurable]]:false } (11.4.1). 259 | var deleteNonConfigurable = function () { 260 | 'use strict'; 261 | var obj = {}; 262 | Object.defineProperty(obj, "name", { 263 | configurable: false 264 | }); 265 | delete obj.name; 266 | } 267 | 268 | testException("error when deleting non configurable", deleteNonConfigurable, TypeError); 269 | 270 | //It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within 271 | //strict code and its Identifier is eval or arguments (12.2.1). 272 | testException("can't use eval as var name", "var eval;", SyntaxError); 273 | testException("can't use arguments as var name", "var arguments;", SyntaxError); 274 | 275 | //Strict mode code may not include a WithStatement. The occurrence of a WithStatement 276 | //in such a context is an SyntaxError (12.10). 277 | testException("can't use with", "with (Math) {round(sqrt(56.67))}", SyntaxError); 278 | 279 | //It is a SyntaxError if a TryStatement with a Catch occurs within strict code and the 280 | //Identifier of the Catch production is eval or arguments (12.14.1) 281 | testException("can't use eval as catch id", "try {'cake'} catch(eval) {}", SyntaxError); 282 | testException("can't use arguments as catch id", "try {'cake'} catch(arguments) {}", SyntaxError); 283 | 284 | //It is a SyntaxError if the identifier eval or arguments appears within a 285 | //FormalParameterList of a strict mode FunctionDeclaration or FunctionExpression (13.1) 286 | testException("can't use eval as formal param", "function(eval) {}", SyntaxError); 287 | testException("can't use arguments as formal param", "function(arguments) {}", SyntaxError); 288 | 289 | //A strict mode function may not have two or more formal parameters that have the same 290 | //name. An attempt to create such a function using a FunctionDeclaration, FunctionExpression, 291 | //or Function constructor is a SyntaxError (13.1, 15.3.2). 292 | testException("can't duplicate formal params", "function(me, me, me) {}", SyntaxError); 293 | 294 | //An implementation may not associate special meanings within strict mode functions to 295 | //properties named caller or arguments of function instances. ECMAScript code may not 296 | //create or modify properties with these names on function objects that correspond to 297 | //strict mode functions (13.2). 298 | testException( 299 | "can't use caller obj of function", 300 | function () {'use strict'; (function () {}).caller}, 301 | TypeError 302 | ); 303 | 304 | //It is a SyntaxError to use within strict mode code the identifiers eval or arguments as 305 | //the Identifier of a FunctionDeclaration or FunctionExpression or as a formal parameter 306 | //name (13.1). Attempting to dynamically define such a strict mode function using the 307 | //Function constructor (15.3.2) will throw a SyntaxError exception. 308 | testException("can't use eval as function name", "function eval() {}", SyntaxError); 309 | testException("can't use arguments as function name", "function arguments() {}", SyntaxError); 310 | 311 | var functionConstructorStr = "new Function('eval', 'use strict')"; 312 | testException("can't use eval as param name via constructor", functionConstructorStr, SyntaxError); 313 | 314 | finishAll(); 315 | 316 | })(); 317 | --------------------------------------------------------------------------------