├── .npmignore ├── .gitignore ├── .travis.yml ├── tests ├── index.html ├── input-cursor.html ├── test.js ├── svg.html ├── mithril.elements-tests.js ├── mock.js └── mithril-tests.js ├── README.md ├── mithril.closure-compiler-externs.js ├── deploy └── cdnjs-package.json ├── LICENSE ├── package.json ├── mithril.elements.min.js ├── mithril.elements.min.js.map ├── mithril.elements.d.ts ├── mithril.elements.js └── Gruntfile.js /.npmignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | archive 4 | npm-debug.log 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | 5 | script: 6 | - grunt test 7 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

Open the console to see the test report

-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/philtoms/mithril.elements.svg?branch=master)](https://travis-ci.org/philtoms/mithril.elements) 2 | 3 | # Mithril.Elements 4 | 5 | A thin wrapper around the Mithril javascript framework that allows you to create composable custom element types: 6 | ```javascript 7 | m('greet', 'Bob') 8 | ``` 9 | becomes: 10 | ```html 11 |
12 | HiBob! 13 |
14 | ``` 15 | For more informtion, please visit [Mithril.Elements Starter Kit](https://github.com/philtoms/mithril-starter-kit) 16 | -------------------------------------------------------------------------------- /mithril.closure-compiler-externs.js: -------------------------------------------------------------------------------- 1 | var m = { 2 | "render": function () {}, 3 | "trust": function () {}, 4 | "module": function () {}, 5 | "redraw": function () {}, 6 | "startComputation": function () {}, 7 | "endComputation": function () {}, 8 | "withAttr": function () {}, 9 | "route": function () {}, 10 | "prop": function () {}, 11 | "deferred": function () {}, 12 | "sync": function () {}, 13 | "request": function () {}, 14 | "deps": function () {}, 15 | "element": function () {}, 16 | "template": function () {} 17 | } 18 | -------------------------------------------------------------------------------- /deploy/cdnjs-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mithril.elements", 3 | "npmName": "mithril.elements", 4 | "version": "$version", 5 | "filename": "mithril.elements.min.js", 6 | "description": "Write beautiful elements web applications using the utterly brilliant Mithril javascript framework.", 7 | "homepage": "http://philtoms.github.io/mithril.elements", 8 | "license": "MIT", 9 | "main": "mithril.elements", 10 | "keywords": [ 11 | "mvc", 12 | "browser", 13 | "custom elements" 14 | ], 15 | "author": "Phil Toms ", 16 | "contributors": [ 17 | ], 18 | "bugs": "https://github.com/philtoms/mithril.elements.js/issues", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/philtoms/mithril.semntic.js.git" 22 | }, 23 | "npmFileMap": [ 24 | { 25 | "basePath": "/", 26 | "files": [ 27 | "mithril.elements.js", 28 | "mithril.elements.min.js", 29 | "mithril.elements.min.map" 30 | ] 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Phil Toms 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mithril.elements", 3 | "version": "0.1.4", 4 | "scripts": { 5 | "test": "grunt test" 6 | }, 7 | "description": "Adds composable custom element types to Mithril", 8 | "main": "mithril.elements.js", 9 | "repository" : { 10 | "type" : "git", 11 | "url" : "http://github.com/philtoms/mithril.elements.git" 12 | }, 13 | "devDependencies": { 14 | "grunt-cli": "*", 15 | "grunt-contrib-copy": "*", 16 | "grunt-contrib-uglify": "*", 17 | "grunt-contrib-clean": "*", 18 | "grunt-contrib-concat": "*", 19 | "grunt-contrib-watch": "~0.6.1", 20 | "grunt-execute": "*", 21 | "grunt-md2html": "*", 22 | "grunt-replace": "*", 23 | "grunt-contrib-qunit": "*", 24 | "grunt-contrib-connect": "~0.7.1", 25 | "grunt-zip": "*", 26 | "grunt-contrib-jshint": "~0.10.0", 27 | "grunt-jscs-checker": "^0.4.4", 28 | "grunt-sauce-tunnel": "^0.2.1", 29 | "load-grunt-config": "^0.9.2", 30 | "merge": "^1.1.3", 31 | "publish": "~0.3.2", 32 | "grunt-saucelabs": "*", 33 | "request": "~2.35.0", 34 | "q": "~1.0.0", 35 | "saucelabs": "~0.1.1", 36 | "sauce-tunnel": "~2.0.6", 37 | "colors": "~0.6.2", 38 | "lodash": "~2.4.1", 39 | "mithril": "~0.1.28" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/input-cursor.html: -------------------------------------------------------------------------------- 1 |

Typing in the fields below should not move the cursor to the end of the input. Especially in Chrome

2 |

All inputs should update with the same value

3 |

Typing in an input should not prevent it from being updated by other inputs

4 |
5 | 6 | 49 | -------------------------------------------------------------------------------- /mithril.elements.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | Mithril.Elements v0.1.4 3 | http://github.com/philtoms/mithril.elements.js 4 | (c) Phil Toms 5 | License: MIT 6 | */ 7 | "use strict";var m=function(a,b){function c(a,b,c){var d="class"in a?"class":"className",e=a[d]||"";return Object.keys(b).forEach(function(d){d.indexOf("class")>=0?e+=" "+b[d]:(c||" ").indexOf(d)<0&&(a[d]=b[d])}),e&&e.trim()&&(a[d]=e.trim()),a}function d(a){this.state=a}var e="[object Array]",f="[object String]",g={}.toString;b=b||require("mithril");var h=b.redraw,i=h.strategy;b.redraw=function(){return"all"===i()&&(j={}),k=0,h.apply(null,arguments)},b.redraw.strategy=i;var j={},k=0,l=function(a,d){var f=a.tag||a,h=[f].concat([].slice.call(arguments,1)),i=b.apply(null,h),m=l.elements[i.tag];if(m&&"$"!==f[0]){d=c(a.attrs||{},i.attrs);var n=d.state,o=a.id||(n&&void 0!==n.id?n.id:void 0!==d.key?d.key:void 0!==d.id?d.id:void 0);o=i.tag+(void 0===o?k++:o);var p=a.id&&a||j[o]||new m.controller(n);j[o]=p;var q=1==i.children.length?i.children[0]:i.children,r=m.view(p,q);r&&(i=r,g.call(i)!==e&&c(i.attrs,d,"state"))}else a.attrs&&c(i.attrs,a.attrs);return i.tag&&"$"===i.tag[0]&&(i.tag=i.tag.substr(1)),i};l.elements={};var m=0;return l.element=function(a,b){if(g.call(a)!==f)throw new Error("tag m.element(tag, module) should be a string");return b.controller=b.controller||d,b.instance=function(c){var d=new b.controller(c);return d.tag=a,d.id="$ctrl_"+a+m++,d},l.elements[a]=b},c(l,b)}("undefined"!=typeof window?window:{},m);"undefined"!=typeof module&&null!==module&&module.exports?module.exports=m:"function"==typeof define&&define.amd&&define(function(){return m}); 8 | //# sourceMappingURL=mithril.elements.min.js.map -------------------------------------------------------------------------------- /tests/test.js: -------------------------------------------------------------------------------- 1 | //make "use strict" and nodejs happy 2 | var window = this 3 | 4 | //test reporting for saucelabs 5 | if (typeof window != "undefined") { 6 | window.global_test_results = { 7 | tests: [], 8 | duration: 0, 9 | passed: 0, 10 | failed: 0 11 | }; 12 | } 13 | 14 | if (!this.console) { 15 | var log = function(value) {document.write("
" + value + "
")} 16 | this.console = {log: log, error: log} 17 | } 18 | 19 | function test(condition) { 20 | var duration = 0 21 | var start = 0 22 | var result = true 23 | test.total++ 24 | 25 | if (typeof performance != "undefined" && performance.now) { 26 | start = performance.now() 27 | } 28 | try { 29 | if (!condition()) throw new Error("failed") 30 | } 31 | catch (e) { 32 | result = false 33 | console.error(e) 34 | test.failures.push(condition) 35 | } 36 | if (typeof performance != "undefined" && performance.now) { 37 | duration = performance.now() - start 38 | } 39 | 40 | window.test_obj = { 41 | name: "" + test.total, 42 | result: result, 43 | duration: duration 44 | } 45 | 46 | if (typeof window != "undefined") { 47 | if (!result) { 48 | window.global_test_results.tests.push(window.test_obj) 49 | } 50 | 51 | window.global_test_results.duration += duration 52 | if (result) { 53 | window.global_test_results.passed++ 54 | } else { 55 | window.global_test_results.failed++ 56 | } 57 | } 58 | } 59 | test.total = 0 60 | test.failures = [] 61 | test.print = function(print) { 62 | for (var i = 0; i < test.failures.length; i++) { 63 | print(test.failures[i].toString()) 64 | } 65 | print("tests: " + test.total + "\nfailures: " + test.failures.length) 66 | 67 | if (test.failures.length > 0) { 68 | throw new Error(test.failures.length + " tests did not pass") 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /mithril.elements.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"mithril.elements.min.js","sources":["mithril.elements.js"],"names":["m","window","mithril","merge","obj1","obj2","filter","classAttrName","classes","Object","keys","forEach","k","indexOf","trim","DefaultController","state","this","ARRAY","STRING","type","toString","require","redraw","strategy","controllers","lastId","apply","arguments","module","attrs","tag","args","concat","slice","call","cell","element","elements","id","undefined","key","ctrl","controller","inner","children","length","c_cell","view","substr","sId","root","Error","instance","exports","define","amd"],"mappings":";;;;;;AAQA,YACA,IAAIA,GAAI,SAAcC,EAAQC,GAU5B,QAASC,GAAMC,EAAKC,EAAKC,GACvB,GAAIC,GAAgB,SAAWH,GAAO,QAAU,YAC5CI,EAAUJ,EAAKG,IAAiB,EAWpC,OAVAE,QAAOC,KAAKL,GAAMM,QAAQ,SAASC,GAC7BA,EAAEC,QAAQ,UAAU,EACtBL,GAAW,IAAMH,EAAKO,IACZN,GAAQ,KAAKO,QAAQD,GAAG,IAClCR,EAAKQ,GAAKP,EAAKO,MAGfJ,GAAWA,EAAQM,SACrBV,EAAKG,GAAeC,EAAQM,QAEvBV,EAqDT,QAASW,GAAkBC,GACzBC,KAAKD,MAAQA,EA3Ef,GAAgCE,GAAQ,iBAAkBC,EAAS,kBAC/DC,KAAUC,QAGdnB,GAAUA,GAAWoB,QAAQ,UAC7B,IAAIC,GAASrB,EAAQqB,OACjBC,EAAWD,EAAOC,QAkBtBtB,GAAQqB,OAAS,WAMf,MAJiB,QAAbC,MACFC,MAEFC,EAAO,EACAH,EAAOI,MAAM,KAAKC,YAG3B1B,EAAQqB,OAAOC,SAAWA,CAE1B,IAAIC,MAAeC,EAAO,EACtB1B,EAAI,SAAS6B,EAAQC,GACvB,GAAIC,GAAMF,EAAOE,KAAOF,EACpBG,GAAQD,GAAKE,UAAUC,MAAMC,KAAKP,UAAU,IAC5CQ,EAAOlC,EAAQyB,MAAM,KAAKK,GAC1BK,EAAUrC,EAAEsC,SAASF,EAAKL,IAE9B,IAAIM,GAAoB,MAATN,EAAI,GAAU,CAC3BD,EAAQ3B,EAAM0B,EAAOC,UAAaM,EAAKN,MACvC,IAAId,GAAQc,EAAMd,MACduB,EAAKV,EAAOU,KAAOvB,GAAoBwB,SAAXxB,EAAMuB,GAAgBvB,EAAMuB,GAAkBC,SAAZV,EAAMW,IAAiBX,EAAMW,IAAkBD,SAAXV,EAAMS,GAAgBT,EAAMS,GAAIC,OACtID,GAAKH,EAAKL,KAAYS,SAALD,EAAgBb,IAAWa,EAK5C,IAAIG,GAAQb,EAAOU,IAAMV,GAAWJ,EAAYc,IAAO,GAAIF,GAAQM,WAAW3B,EAC9ES,GAAYc,GAAIG,CAChB,IAAIE,GAA8B,GAAtBR,EAAKS,SAASC,OAAWV,EAAKS,SAAS,GAAGT,EAAKS,SACvDE,EAASV,EAAQW,KAAKN,EAAME,EAC5BG,KACFX,EAAKW,EACD3B,EAAKe,KAAKC,KAAUlB,GACtBf,EAAMiC,EAAKN,MAAMA,EAAM,cAKpBD,GAAOC,OACd3B,EAAMiC,EAAKN,MAAOD,EAAOC,MAO3B,OAHIM,GAAKL,KAAqB,MAAdK,EAAKL,IAAI,KACvBK,EAAKL,IAAIK,EAAKL,IAAIkB,OAAO,IAEpBb,EAQTpC,GAAEsC,WAEF,IAAIY,GAAI,CAqBR,OApBAlD,GAAEqC,QAAU,SAASc,EAAMtB,GACzB,GAAIT,EAAKe,KAAKgB,KAAUhC,EAAQ,KAAM,IAAIiC,OAAM,gDAehD,OAZAvB,GAAOc,WAAad,EAAOc,YAAc5B,EAGzCc,EAAOwB,SAAW,SAASrC,GACzB,GAAI0B,GAAO,GAAIb,GAAOc,WAAW3B,EAGjC,OAFA0B,GAAKX,IAAMoB,EACXT,EAAKH,GAAK,SAAWY,EAAOD,IACrBR,GAKD1C,EAAEsC,SAASa,GAAQtB,GAItB1B,EAAMH,EAAEE,IAEG,mBAAVD,QAAwBA,UAAYD,EAEzB,oBAAV6B,SAAoC,OAAXA,QAAmBA,OAAOyB,QAASzB,OAAOyB,QAAUtD,EAC9D,kBAAVuD,SAAwBA,OAAOC,KAAKD,OAAO,WAAY,MAAOvD"} -------------------------------------------------------------------------------- /mithril.elements.d.ts: -------------------------------------------------------------------------------- 1 | //Mithril.Element type definitions for Typescript 2 | 3 | interface MithrilStatic { 4 | (selector: string, attributes: Object, children?: any): MithrilVirtualElement; 5 | (selector: string, children?: any): MithrilVirtualElement; 6 | (selector: string, attributes: Object, children: any, state: any): MithrilStatefullVirtualElement; 7 | prop(value?: any): (value?: any) => any; 8 | withAttr(property: string, callback: (value: any) => void): (e: Event) => any; 9 | module(rootElement: Node, module: MithrilModule): Object; 10 | trust(html: string): String; 11 | render(rootElement: Element, children?: any): void; 12 | render(rootElement: HTMLDocument, children?: any): void; 13 | redraw: { 14 | (): void; 15 | strategy(key: string); 16 | }; 17 | route: { 18 | (rootElement:Element, defaultRoute:string, routes:{ [key: string]: MithrilModule }): void 19 | (element: Element, isInitialized: boolean): void; 20 | (rootElement:HTMLDocument, defaultRoute:string, routes:{ [key: string]: MithrilModule }): void; 21 | (path:string, params?:any, shouldReplaceHistory?:boolean): void; 22 | (): string; 23 | param(key:string): string; 24 | mode: string; 25 | }; 26 | request(options: MithrilXHROptions): MithrilPromise; 27 | deferred(): MithrilDeferred; 28 | sync(promises: MithrilPromise[]): MithrilPromise; 29 | startComputation(): void; 30 | endComputation(): void; 31 | deps(Object: any): Object; 32 | 33 | element(name: string, module: MithrilModule): Object; 34 | template(selector: string, attributes: Object, children?: any): MithrilVirtualElement; 35 | 36 | } 37 | 38 | interface MithrilVirtualElement { 39 | tag: string; 40 | attrs: Object; 41 | children: any; 42 | } 43 | 44 | interface MithrilStatefullVirtualElement { 45 | tag: string; 46 | attrs: Object; 47 | children: any; 48 | state: any; 49 | } 50 | 51 | interface MithrilModule { 52 | controller: Function; 53 | view: Function; 54 | } 55 | 56 | interface MithrilDeferred { 57 | resolve(value?: any): void; 58 | reject(value?: any): void; 59 | promise: MithrilPromise; 60 | } 61 | 62 | interface MithrilPromise { 63 | (value?: any): any; 64 | then(successCallback?: (value: any) => any, errorCallback?: (value: any) => any): MithrilPromise; 65 | } 66 | 67 | interface MithrilXHROptions { 68 | method: string; 69 | url: string; 70 | user?: string; 71 | password?: string; 72 | data?: any; 73 | background?: boolean; 74 | unwrapSuccess?(data: any): any; 75 | unwrapError?(data: any): any; 76 | serialize?(dataToSerialize: any): string; 77 | deserialize?(dataToDeserialize: string): any; 78 | extract?(xhr: XMLHttpRequest, options: MithrilXHROptions): string; 79 | type?(data: Object): void; 80 | config?(xhr: XMLHttpRequest, options: MithrilXHROptions): XMLHttpRequest; 81 | } 82 | 83 | declare var Mithril: MithrilStatic; 84 | declare var m: MithrilStatic; 85 | 86 | declare module 'mithril' { 87 | export = MithrilStatic; 88 | } 89 | -------------------------------------------------------------------------------- /mithril.elements.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Mithril.Elements 3 | * Copyright (c) 2014 Phil Toms (@PhilToms3). 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE.txt file in the root directory of this source tree. 7 | */ 8 | 9 | 'use strict'; 10 | var m = (function app(window, mithril) { 11 | 12 | var OBJECT = '[object Object]', ARRAY = '[object Array]', STRING = '[object String]', FUNCTION = "[object Function]"; 13 | var type = {}.toString; 14 | 15 | // save the mithril API 16 | mithril = mithril || require('mithril'); 17 | var redraw = mithril.redraw; 18 | var strategy = redraw.strategy; 19 | 20 | function merge(obj1,obj2,filter){ 21 | var classAttrName = 'class' in obj1 ? 'class' : 'className'; 22 | var classes = obj1[classAttrName]|| ''; 23 | Object.keys(obj2).forEach(function(k){ 24 | if (k.indexOf('class')>=0){ 25 | classes += ' ' + obj2[k]; 26 | } else if ((filter||' ').indexOf(k)<0) { 27 | obj1[k] = obj2[k]; 28 | } 29 | }); 30 | if (classes && classes.trim()) { 31 | obj1[classAttrName]=classes.trim(); 32 | } 33 | return obj1; 34 | } 35 | 36 | mithril.redraw = function() { 37 | // key into mithril page lifecycle 38 | if (strategy()==='all'){ 39 | controllers={}; 40 | } 41 | lastId=0; 42 | return redraw.apply(null,arguments); 43 | }; 44 | 45 | mithril.redraw.strategy = strategy; 46 | 47 | var controllers={},lastId=0; 48 | var m = function(module, attrs, children) { 49 | var tag = module.tag || module; 50 | var args = [tag].concat([].slice.call(arguments,1)); 51 | var cell = mithril.apply(null,args); 52 | var element = m.elements[cell.tag]; 53 | // pass through if not registered or escaped 54 | if (element && tag[0]!=='$') { 55 | attrs = merge(module.attrs || {}, cell.attrs); 56 | var state = attrs.state; 57 | var id = module.id || (state && state.id!==undefined? state.id : (attrs.key!==undefined? attrs.key : (attrs.id!==undefined? attrs.id :undefined))); 58 | id = cell.tag + (id===undefined? lastId++ : id); 59 | // once-only element initialization. But note: 60 | // module.id - singleton 61 | // controllers[id] - cached 62 | // default - new instance 63 | var ctrl = (module.id && module) || controllers[id] || new element.controller(state); 64 | controllers[id]=ctrl; 65 | var inner = cell.children.length==1? cell.children[0]:cell.children; 66 | var c_cell = element.view(ctrl, inner); 67 | if (c_cell){ 68 | cell=c_cell; 69 | if (type.call(cell) !== ARRAY) { 70 | merge(cell.attrs,attrs,'state'); 71 | } 72 | } 73 | } 74 | // merge outer over inner 75 | else if (module.attrs){ 76 | merge(cell.attrs, module.attrs); 77 | } 78 | 79 | // tidy up tag 80 | if (cell.tag && cell.tag[0]==='$'){ 81 | cell.tag=cell.tag.substr(1); 82 | } 83 | return cell; 84 | }; 85 | 86 | function DefaultController(state){ 87 | this.state = state; 88 | } 89 | 90 | // expose registration dictionary 91 | m.elements = {}; 92 | 93 | var sId=0; 94 | m.element = function(root, module){ 95 | if (type.call(root) !== STRING) throw new Error('tag m.element(tag, module) should be a string'); 96 | 97 | // all elements have controllers 98 | module.controller = module.controller || DefaultController; 99 | 100 | // add a programmable interface to the element 101 | module.instance = function(state){ 102 | var ctrl = new module.controller(state); 103 | ctrl.tag = root; 104 | ctrl.id = '$ctrl_' + root + sId++; 105 | return ctrl; 106 | }; 107 | 108 | // nothing more to do here, element initialization is lazily 109 | // deferred to first redraw 110 | return (m.elements[root] = module); 111 | }; 112 | 113 | // build the new API 114 | return merge(m,mithril); 115 | 116 | })(typeof window != "undefined" ? window : {},m); 117 | 118 | if (typeof module != "undefined" && module !== null && module.exports) module.exports = m; 119 | else if (typeof define == "function" && define.amd) define(function() {return m}); -------------------------------------------------------------------------------- /tests/svg.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SVG test 5 | 21 | 22 | 23 |

Since it's not possible to test SVG functionality from a NodeJS environment, this page can be used to test it in a browser.

24 |

This page should contain:

25 | 33 |

The links should open in a new tab. All items should display title tooltips when hovered over.

34 | 35 |
36 | 37 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /tests/mithril.elements-tests.js: -------------------------------------------------------------------------------- 1 | function testMithrilElements(mock) { 2 | m.deps(mock) 3 | 4 | var custom = { 5 | controller:function(data){ 6 | if (data) data.spy+=1; 7 | this.data=data || {}; 8 | }, 9 | view: function(ctrl,ctx){ 10 | if (ctrl.data) ctrl.data.spy++; 11 | if (typeof ctx === 'function') return m('div',ctx('test1')); 12 | if (ctx.tag){ 13 | return m('div',m(ctx)); 14 | } 15 | return m('div',ctx); 16 | } 17 | } 18 | var data; 19 | function reset(){ 20 | data={spy:0}; 21 | m.redraw.strategy('all'); 22 | m.redraw(); 23 | } 24 | 25 | //m.element 26 | test(function() {return m.element("custom", custom)}) //as long as it doesn't throw errors, it's fine 27 | 28 | // m.element - default ctrl 29 | test(function() { 30 | m.element("custom1", { 31 | view: function(ctrl){ 32 | ctrl.state.spy++; 33 | } 34 | }) 35 | var data = {spy:0}; 36 | m("custom1", {state:data}) 37 | return data.spy===1; 38 | }) 39 | 40 | //lifetime - cached controller 41 | test(function() { 42 | reset(); 43 | m("custom#1",{state:data}) 44 | m("custom#1",{state:data}) 45 | return data.spy==3; 46 | }) 47 | 48 | //lifetime - unique controller 49 | test(function() { 50 | reset(); 51 | m("custom#1",{state:data}) 52 | m("custom#2",{state:data}) 53 | return data.spy==4; 54 | }) 55 | 56 | //lifetime - unique controller (lastId) 57 | test(function() { 58 | reset(); 59 | m("custom",{state:data}) 60 | m("custom",{state:data}) 61 | return data.spy==4; 62 | }) 63 | 64 | //lastId reset 65 | test(function() { 66 | reset(); 67 | m("custom",{state:data}) 68 | reset(); 69 | m("custom",{state:data}) 70 | return data.spy==2; 71 | }) 72 | 73 | //m - selector 74 | test(function() {return m("custom").tag === "div"}) 75 | test(function() {return m("custom.foo").tag === "div"}) 76 | test(function() {return m("custom.foo").attrs.className === "foo"}) 77 | 78 | test(function() {return m("custom#foo").attrs.id === "foo"}) 79 | test(function() {return m("custom#foo.bar").attrs.id === "foo"}) 80 | test(function() {return m("custom#foo.bar").attrs.className === "bar"}) 81 | 82 | // m - children 83 | test(function() {return m("custom", "test").children[0] === "test"}) 84 | test(function() {return m("custom", "test", "test2").children[1] === "test2"}) 85 | test(function() {return m("custom", ["test"]).children[0] === "test"}) 86 | 87 | // m - functor children 88 | test(function() {return m("custom", function(data){ return data }).children[0] === "test1"}) 89 | test(function() {return m("custom", {}, function(data){ return data }).children[0] === "test1"}) 90 | 91 | //m - passthrough 92 | test(function() {return m("div").tag === "div"}) 93 | test(function() {return m("div.foo").tag === "div"}) 94 | test(function() {return m("div.foo").attrs.className === "foo"}) 95 | test(function() {return m("div", "test").children[0] === "test"}) 96 | test(function() {return m("div", "test", "test2").children[1] === "test2"}) 97 | test(function() {return m("div", ["test"]).children[0] === "test"}) 98 | test(function() {return m("div", {title: "bar"}, "test").attrs.title === "bar"}) 99 | test(function() {return m("div", {title: "bar"}, "test").children[0] === "test"}) 100 | test(function() {return m("div", {title: "bar"}, ["test"]).children[0] === "test"}) 101 | test(function() {return m("div", {title: "bar"}, m("div")).children[0].tag === "div"}) 102 | test(function() {return m("div", {title: "bar"}, [m("div")]).children[0].tag === "div"}) 103 | test(function() {return m("div", {title: "bar"}, "test0", "test1", "test2", "test3").children[3] === "test3"}) // splat 104 | test(function() {return m("div", {title: "bar"}, m("div"), m("i"), m("span")).children[2].tag === "span"}) 105 | test(function() {return m("div", ["a", "b"]).children.length === 2}) 106 | test(function() {return m("div", [m("div")]).children[0].tag === "div"}) 107 | test(function() {return m("div", m("div")).children[0].tag === "div"}) //yes, this is expected behavior: see method signature 108 | test(function() {return m("div", [{foo: "bar"}])}) //as long as it doesn't throw errors, it's fine 109 | 110 | //m - state 111 | test(function() { 112 | reset(); 113 | m("custom", {state:data}) 114 | return data.spy===2 115 | }) 116 | test(function() {return m("custom", {state:data}).attrs.state===undefined}) 117 | 118 | // m - template 119 | test(function() {return m("custom", [m('$test1')]).children[0].tag === "test1"}) 120 | 121 | // m - compiled template 122 | test(function() {return m("custom", [m('$test1.foo')]).children[0].attrs.className === "foo"}) 123 | 124 | } 125 | //mock 126 | testMithrilElements(mock.window); 127 | 128 | test.print(function(value) {console.log(value)}) 129 | -------------------------------------------------------------------------------- /tests/mock.js: -------------------------------------------------------------------------------- 1 | if (!Array.prototype.indexOf) { 2 | Array.prototype.indexOf = function(item) { 3 | for (var i = 0; i < this.length; i++) { 4 | if (this[i] === item) return i 5 | } 6 | return -1 7 | } 8 | } 9 | if (!Array.prototype.map) { 10 | Array.prototype.map = function(callback) { 11 | var results = [] 12 | for (var i = 0; i < this.length; i++) { 13 | results[i] = callback(this[i], i, this) 14 | } 15 | return results 16 | } 17 | } 18 | if (!Array.prototype.filter) { 19 | Array.prototype.filter = function(callback) { 20 | var results = [] 21 | for (var i = 0; i < this.length; i++) { 22 | if (callback(this[i], i, this)) results.push(this[i]) 23 | } 24 | return results 25 | } 26 | } 27 | if (!Object.keys) { 28 | Object.keys = function() { 29 | var keys = [] 30 | for (var i in this) keys.push(i) 31 | return keys 32 | } 33 | } 34 | 35 | var mock = {} 36 | mock.window = (function() { 37 | var window = {} 38 | window.document = {} 39 | window.document.childNodes = [] 40 | window.document.createElement = function(tag) { 41 | return { 42 | style: {}, 43 | childNodes: [], 44 | nodeType: 1, 45 | nodeName: tag.toUpperCase(), 46 | appendChild: window.document.appendChild, 47 | removeChild: window.document.removeChild, 48 | replaceChild: window.document.replaceChild, 49 | insertBefore: function(node, reference) { 50 | node.parentNode = this 51 | var referenceIndex = this.childNodes.indexOf(reference) 52 | var index = this.childNodes.indexOf(node) 53 | if (index > -1) this.childNodes.splice(index, 1) 54 | if (referenceIndex < 0) this.childNodes.push(node) 55 | else this.childNodes.splice(referenceIndex, 0, node) 56 | }, 57 | insertAdjacentHTML: function(position, html) { 58 | //todo: accept markup 59 | if (position == "beforebegin") { 60 | this.parentNode.insertBefore(window.document.createTextNode(html), this) 61 | } 62 | else if (position == "beforeend") { 63 | this.appendChild(window.document.createTextNode(html)) 64 | } 65 | }, 66 | setAttribute: function(name, value) { 67 | this[name] = value.toString() 68 | }, 69 | setAttributeNS: function(namespace, name, value) { 70 | this.namespaceURI = namespace 71 | this[name] = value.toString() 72 | }, 73 | getAttribute: function(name, value) { 74 | return this[name] 75 | }, 76 | addEventListener: function () {}, 77 | removeEventListener: function () {} 78 | } 79 | } 80 | window.document.createElementNS = function(namespace, tag) { 81 | var element = window.document.createElement(tag) 82 | element.namespaceURI = namespace 83 | return element 84 | } 85 | window.document.createTextNode = function(text) { 86 | return {nodeValue: text.toString()} 87 | } 88 | window.document.documentElement = window.document.createElement("html") 89 | window.document.replaceChild = function(newChild, oldChild) { 90 | var index = this.childNodes.indexOf(oldChild) 91 | if (index > -1) this.childNodes.splice(index, 1, newChild) 92 | else this.childNodes.push(newChild) 93 | newChild.parentNode = this 94 | oldChild.parentNode = null 95 | } 96 | window.document.appendChild = function(child) { 97 | var index = this.childNodes.indexOf(child) 98 | if (index > -1) this.childNodes.splice(index, 1) 99 | this.childNodes.push(child) 100 | child.parentNode = this 101 | } 102 | window.document.removeChild = function(child) { 103 | var index = this.childNodes.indexOf(child) 104 | this.childNodes.splice(index, 1) 105 | child.parentNode = null 106 | } 107 | //getElementsByTagName is only used by JSONP tests, it's not required by Mithril 108 | window.document.getElementsByTagName = function(name){ 109 | name = name.toLowerCase(); 110 | var out = []; 111 | 112 | var traverse = function(node){ 113 | if(node.childNodes && node.childNodes.length > 0){ 114 | node.childNodes.map(function(curr){ 115 | if(curr.nodeName.toLowerCase() === name) 116 | out.push(curr); 117 | traverse(curr); 118 | }); 119 | } 120 | }; 121 | 122 | traverse(window.document); 123 | return out; 124 | } 125 | window.scrollTo = function() {} 126 | window.cancelAnimationFrame = function() {} 127 | window.requestAnimationFrame = function(callback) { 128 | window.requestAnimationFrame.$callback = callback 129 | return window.requestAnimationFrame.$id++ 130 | } 131 | window.requestAnimationFrame.$id = 1 132 | window.requestAnimationFrame.$resolve = function() { 133 | if (window.requestAnimationFrame.$callback) { 134 | var callback = window.requestAnimationFrame.$callback 135 | window.requestAnimationFrame.$callback = null 136 | callback() 137 | } 138 | } 139 | window.XMLHttpRequest = (function() { 140 | var request = function() { 141 | this.$headers = {} 142 | this.setRequestHeader = function(key, value) { 143 | this.$headers[key] = value 144 | } 145 | this.open = function(method, url) { 146 | this.method = method 147 | this.url = url 148 | } 149 | this.send = function() { 150 | this.responseText = JSON.stringify(this) 151 | this.readyState = 4 152 | this.status = 200 153 | request.$instances.push(this) 154 | } 155 | } 156 | request.$instances = [] 157 | return request 158 | }()) 159 | window.location = {search: "", pathname: "", hash: ""}, 160 | window.history = {} 161 | window.history.$$length = 0 162 | window.history.pushState = function(data, title, url) { 163 | window.history.$$length++ 164 | window.location.pathname = window.location.search = window.location.hash = url 165 | }, 166 | window.history.replaceState = function(data, title, url) { 167 | window.location.pathname = window.location.search = window.location.hash = url 168 | } 169 | return window 170 | }()) -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | _ = require('lodash'); 3 | 4 | var version = "0.1.4"; 5 | 6 | var inputFolder = "./docs"; 7 | var tempFolder = "./temp"; 8 | var archiveFolder = "./archive"; 9 | var outputFolder = "../mithril.elements"; 10 | 11 | 12 | var sauceBrowsers =[ 13 | { browserName: 'firefox', version: '19', platform: 'XP' }, 14 | { browserName: "internet explorer", platform: "XP", version: "6"}, 15 | { browserName: "safari", platform: "OS X 10.9", version: "7"}, 16 | { browserName: "iPad", platform: "OS X 10.9", version: "7.1"}, 17 | { browserName: "opera", platform: "Linux", version: "12"}, 18 | { browserName: "chrome", platform: "XP", version: "26"}, 19 | { browserName: "chrome", platform: "Windows 8", version: "26"} 20 | ]; 21 | 22 | var sauceOnTestComplete = function(result, callback) { 23 | var request = require('request'); 24 | 25 | var user = process.env.SAUCE_USERNAME; 26 | var pass = process.env.SAUCE_ACCESS_KEY; 27 | 28 | request.put({ 29 | url: ['https://saucelabs.com/rest/v1', user, 'jobs', result.job_id].join('/'), 30 | auth: { user: user, pass: pass }, 31 | json: { passed: result.passed } 32 | }, function (error, response, body) { 33 | if (error) { 34 | callback(error); 35 | } else if (response.statusCode !== 200) { 36 | callback(new Error('Unexpected response status: ' 37 | + response.statusCode + "\n ")); 38 | } else { 39 | callback(null, result.passed); 40 | } 41 | }); 42 | }; 43 | 44 | var sauceBaseOptions = { 45 | username: process.env.SAUCE_USERNAME, 46 | key: process.env.SAUCE_ACCESS_KEY, 47 | testname: "Mithril Tests " + new Date().toJSON(), 48 | browsers: sauceBrowsers, 49 | sauceConfig: { 50 | "record-video": false, 51 | "record-screenshots": false, 52 | }, 53 | build: process.env.TRAVIS_JOB_ID, 54 | onTestComplete: sauceOnTestComplete, 55 | tunnelTimeout: 5, 56 | }; 57 | var sauceCustomOptions = { 58 | testname: "Mithril Custom Tests "+ new Date().toJSON(), 59 | urls: ["http://127.0.0.1:8000/tests/index.html"], 60 | }; 61 | _.assign(sauceCustomOptions, sauceBaseOptions); 62 | var sauceQunitOptions = { 63 | testname: "qUnit Tests "+ new Date().toJSON(), 64 | urls: ["http://127.0.0.1:8000/tests/e2e/test.html"], 65 | }; 66 | _.assign(sauceQunitOptions, sauceBaseOptions); 67 | 68 | var currentVersionArchiveFolder = archiveFolder + "/v" + version; 69 | grunt.initConfig({ 70 | uglify: { 71 | options: {banner: "/*\nMithril.Elements v" + version + "\nhttp://github.com/philtoms/mithril.elements.js\n(c) Phil Toms\nLicense: MIT\n*/", sourceMap: true}, 72 | mithril: {src: "mithril.elements.js", dest: "mithril.elements.min.js"} 73 | }, 74 | concat: { 75 | test: {src: ["./node_modules/mithril/mithril.js", "mithril.elements.js", "./tests/test.js", "./tests/mock.js", "./tests/mithril.elements-tests.js", "./tests/mithril-tests.js"], dest: currentVersionArchiveFolder + "/mithril.elements-tests.js"} 76 | }, 77 | zip: { 78 | distribution: { 79 | cwd: currentVersionArchiveFolder + "/", 80 | src: [currentVersionArchiveFolder + "/mithril.elements.min.js", currentVersionArchiveFolder + "/mithril.elements.min.js.map", currentVersionArchiveFolder + "/mithril.elements.js"], 81 | dest: currentVersionArchiveFolder + "/mithril.elements.min.zip" 82 | } 83 | }, 84 | replace: { 85 | options: {force: true, patterns: [{match: /\.md/g, replacement: ".html"}, {match: /\$version/g, replacement: version}]}, 86 | links: {expand: true, flatten: true, src: [tempFolder + "/**/*.html"], dest: currentVersionArchiveFolder + "/"}, 87 | index: {src: inputFolder + "/layout/index.html", dest: currentVersionArchiveFolder + "/index.html"}, 88 | commonjs: {expand: true, flatten: true, src: [inputFolder + "/layout/*.json"], dest: currentVersionArchiveFolder}, 89 | cdnjs: {src: "deploy/cdnjs-package.json", dest: "../cdnjs/ajax/libs/mithril.elements/package.json"} 90 | }, 91 | copy: { 92 | style: {src: inputFolder + "/layout/style.css", dest: currentVersionArchiveFolder + "/style.css"}, 93 | pages: {src: inputFolder + "/layout/pages.json", dest: currentVersionArchiveFolder + "/pages.json"}, 94 | lib: {expand: true, cwd: inputFolder + "/layout/lib/", src: "./**", dest: currentVersionArchiveFolder + "/lib/"}, 95 | tools: {expand: true, cwd: inputFolder + "/layout/tools/", src: "./**", dest: currentVersionArchiveFolder + "/tools/"}, 96 | comparisons: {expand: true, cwd: inputFolder + "/layout/comparisons/", src: "./**", dest: currentVersionArchiveFolder + "/comparisons/"}, 97 | unminified: {src: "mithril.elements.js", dest: currentVersionArchiveFolder + "/mithril.elements.js"}, 98 | minified: {src: "mithril.elements.min.js", dest: currentVersionArchiveFolder + "/mithril.elements.min.js"}, 99 | map: {src: "mithril.min.elements.js.map", dest: currentVersionArchiveFolder + "/mithril.elements.min.js.map"}, 100 | typescript: {src: "mithril.elements.d.ts", dest: currentVersionArchiveFolder + "/mithril.elements.d.ts"}, 101 | publish: {expand: true, cwd: currentVersionArchiveFolder, src: "./**", dest: outputFolder}, 102 | archive: {expand: true, cwd: currentVersionArchiveFolder, src: "./**", dest: outputFolder + "/archive/v" + version}, 103 | }, 104 | execute: { 105 | tests: {src: [currentVersionArchiveFolder + "/mithril.elements-tests.js"]} 106 | }, 107 | qunit: { 108 | all: ['tests/e2e/**/*.html'] 109 | }, 110 | "saucelabs-custom": { 111 | all:{ 112 | options: sauceCustomOptions 113 | } 114 | }, 115 | "saucelabs-qunit": { 116 | all:{ 117 | options: sauceQunitOptions 118 | } 119 | }, 120 | watch: {}, 121 | 122 | connect: { 123 | server: { 124 | options: { 125 | port: 8888, 126 | base: '.' 127 | } 128 | } 129 | }, 130 | clean: { 131 | options: {force: true}, 132 | generated: [tempFolder] 133 | } 134 | }); 135 | 136 | grunt.loadNpmTasks("grunt-contrib-clean"); 137 | grunt.loadNpmTasks('grunt-contrib-concat'); 138 | grunt.loadNpmTasks("grunt-contrib-copy"); 139 | grunt.loadNpmTasks("grunt-contrib-uglify"); 140 | grunt.loadNpmTasks('grunt-execute'); 141 | grunt.loadNpmTasks("grunt-replace"); 142 | grunt.loadNpmTasks('grunt-zip'); 143 | grunt.loadNpmTasks('grunt-contrib-qunit'); 144 | grunt.loadNpmTasks('grunt-contrib-connect'); 145 | grunt.loadNpmTasks('grunt-saucelabs'); 146 | 147 | grunt.registerTask("build", ["test", "uglify", "zip", "replace", "copy", "clean"]); 148 | grunt.registerTask("test", ["concat", "execute"]); 149 | grunt.registerTask("default", ["build"]); 150 | 151 | grunt.registerTask("sauce-qunit", ["connect", "saucelabs-qunit"]); 152 | grunt.registerTask("sauce-custom", ["connect", "saucelabs-custom"]); 153 | grunt.registerTask("sauce-all", ["connect", "saucelabs-qunit", "saucelabs-custom"]); 154 | }; 155 | -------------------------------------------------------------------------------- /tests/mithril-tests.js: -------------------------------------------------------------------------------- 1 | function testMithril(mock) { 2 | m.deps(mock) 3 | 4 | //m 5 | test(function() {return m("div").tag === "div"}) 6 | test(function() {return m(".foo").tag === "div"}) 7 | test(function() {return m(".foo").attrs.className === "foo"}) 8 | test(function() {return m("[title=bar]").tag === "div"}) 9 | test(function() {return m("[title=bar]").attrs.title === "bar"}) 10 | test(function() {return m("[title=\'bar\']").attrs.title === "bar"}) 11 | test(function() {return m("[title=\"bar\"]").attrs.title === "bar"}) 12 | test(function() {return m("div", "test").children[0] === "test"}) 13 | test(function() {return m("div", "test", "test2").children[1] === "test2"}) 14 | test(function() {return m("div", ["test"]).children[0] === "test"}) 15 | test(function() {return m("div", {title: "bar"}, "test").attrs.title === "bar"}) 16 | test(function() {return m("div", {title: "bar"}, "test").children[0] === "test"}) 17 | test(function() {return m("div", {title: "bar"}, ["test"]).children[0] === "test"}) 18 | test(function() {return m("div", {title: "bar"}, m("div")).children[0].tag === "div"}) 19 | test(function() {return m("div", {title: "bar"}, [m("div")]).children[0].tag === "div"}) 20 | test(function() {return m("div", {title: "bar"}, "test0", "test1", "test2", "test3").children[3] === "test3"}) // splat 21 | test(function() {return m("div", {title: "bar"}, m("div"), m("i"), m("span")).children[2].tag === "span"}) 22 | test(function() {return m("div", ["a", "b"]).children.length === 2}) 23 | test(function() {return m("div", [m("div")]).children[0].tag === "div"}) 24 | test(function() {return m("div", m("div")).children[0].tag === "div"}) //yes, this is expected behavior: see method signature 25 | test(function() {return m("div", [undefined]).tag === "div"}) 26 | test(function() {return m("div", [{foo: "bar"}])}) //as long as it doesn't throw errors, it's fine 27 | test(function() {return m("svg", [m("g")])}) 28 | test(function() {return m("svg", [m("a[href='http://google.com']")])}) 29 | test(function() {return m(".foo", {"class": "bar"}).attrs["class"] == "foo bar"}) 30 | test(function() {return m(".foo", {className: "bar"}).attrs.className == "foo bar"}) 31 | test(function() {return m(".foo", {className: ""}).attrs.className == "foo"}) 32 | test(function() {return m("div", {className: ""}).attrs.className === ""}) //https://github.com/lhorie/mithril.js/issues/382 and 512 33 | test(function() {return m("div", {class: ""}).attrs.className === undefined}) 34 | test(function() {return m("div", {className: ""}).attrs.class === undefined}) 35 | test(function() {return m("div", {class: ""}).attrs.class === ""}) 36 | test(function() {return m("div", [1, 2, 3], 4).children.length === 2}) 37 | test(function() {return m("div", [1, 2, 3], 4).children[0].length === 3}) 38 | test(function() {return m("div", [1, 2, 3], 4).children[1] === 4}) 39 | test(function() {return m("div", [1, 2, 3]).children.length === 3}) 40 | test(function() {return m("div", [1, 2, 3], [4, 5, 6, 7]).children.length === 2}) 41 | test(function() {return m("div", [1, 2, 3], [4, 5, 6, 7]).children[0].length === 3}) 42 | test(function() {return m("div", [1, 2, 3], [4, 5, 6, 7]).children[1].length === 4}) 43 | test(function() {return m("div", [1], [2], [3]).children.length === 3}) 44 | 45 | //m.module 46 | test(function() { 47 | var root = mock.document.createElement("div") 48 | var whatever = 1 49 | var app = { 50 | view: function() { 51 | return [ 52 | whatever % 2 ? m('span', '% 2') : undefined, 53 | m('div', 'bugs'), 54 | m('a'), 55 | ] 56 | } 57 | } 58 | m.module(root, app) 59 | mock.requestAnimationFrame.$resolve() 60 | 61 | whatever++ 62 | m.redraw() 63 | mock.requestAnimationFrame.$resolve() 64 | 65 | whatever++ 66 | m.redraw() 67 | mock.requestAnimationFrame.$resolve() 68 | 69 | return root.childNodes.length 70 | }) 71 | 72 | test(function() { 73 | mock.requestAnimationFrame.$resolve() 74 | 75 | var root1 = mock.document.createElement("div") 76 | var mod1 = m.module(root1, { 77 | controller: function() {this.value = "test1"}, 78 | view: function(ctrl) {return ctrl.value} 79 | }) 80 | 81 | var root2 = mock.document.createElement("div") 82 | var mod2 = m.module(root2, { 83 | controller: function() {this.value = "test2"}, 84 | view: function(ctrl) {return ctrl.value} 85 | }) 86 | 87 | mock.requestAnimationFrame.$resolve() 88 | 89 | return (root1.childNodes[0].nodeValue === "test1" && root2.childNodes[0].nodeValue === "test2") 90 | && (mod1.value && mod1.value === "test1") && (mod2.value && mod2.value === "test2") 91 | }) 92 | test(function() { 93 | mock.requestAnimationFrame.$resolve() 94 | 95 | var root = mock.document.createElement("div") 96 | var unloaded = false 97 | var mod = m.module(root, { 98 | controller: function() { 99 | this.value = "test1" 100 | this.onunload = function() { 101 | unloaded = true 102 | } 103 | }, 104 | view: function(ctrl) {return ctrl.value} 105 | }) 106 | 107 | mock.requestAnimationFrame.$resolve() 108 | 109 | m.module(root, null) 110 | 111 | mock.requestAnimationFrame.$resolve() 112 | 113 | return unloaded 114 | }) 115 | test(function() { 116 | var root = mock.document.createElement("div") 117 | var module = {}, unloaded = false 118 | module.controller = function() { 119 | this.onunload = function() {unloaded = true} 120 | } 121 | module.view = function() {} 122 | m.module(root, module) 123 | m.module(root, {controller: function() {}, view: function() {}}) 124 | 125 | return unloaded === true 126 | }) 127 | test(function() { 128 | mock.requestAnimationFrame.$resolve() 129 | 130 | var root = mock.document.createElement("div") 131 | var initCount = 0 132 | var module = {} 133 | module.view = function() { 134 | return m("div", {config: function(el, init) { 135 | if (!init) initCount++ 136 | }}) 137 | } 138 | m.module(root, module) 139 | 140 | mock.requestAnimationFrame.$resolve() 141 | 142 | m.redraw() 143 | 144 | mock.requestAnimationFrame.$resolve() 145 | 146 | return initCount == 1 147 | }) 148 | test(function() { 149 | var root = mock.document.createElement("div") 150 | 151 | var dom = mock.document.createElement("div") 152 | 153 | var show = true 154 | 155 | var module = { 156 | view: function() { 157 | return [ 158 | m(".foo", {key: 1, config: test, onclick: function() {show = !show}}), 159 | show ? m(".bar", {key: 2}) : null 160 | ] 161 | } 162 | } 163 | 164 | function test(el, init) { 165 | if (!init) { 166 | root.appendChild(dom) 167 | } 168 | } 169 | 170 | m.module(root, module) 171 | 172 | mock.requestAnimationFrame.$resolve() 173 | 174 | show = false 175 | m.redraw() 176 | 177 | mock.requestAnimationFrame.$resolve() 178 | 179 | show = true 180 | m.redraw() 181 | 182 | mock.requestAnimationFrame.$resolve() 183 | 184 | return root.childNodes.length == 3 185 | }) 186 | m.redraw.strategy(undefined) //teardown for m.module tests 187 | 188 | //m.withAttr 189 | test(function() { 190 | var value 191 | var handler = m.withAttr("test", function(data) {value = data}) 192 | handler({currentTarget: {test: "foo"}}) 193 | return value === "foo" 194 | }) 195 | 196 | //m.trust 197 | test(function() {return m.trust("test").valueOf() === "test"}) 198 | 199 | //m.render 200 | test(function() { 201 | var root = mock.document.createElement("div") 202 | m.render(root, "test") 203 | return root.childNodes[0].nodeValue === "test" 204 | }) 205 | test(function() { 206 | var root = mock.document.createElement("div") 207 | m.render(root, m("div", {"class": "a"})) 208 | var elementBefore = root.childNodes[0] 209 | m.render(root, m("div", {"class": "b"})) 210 | var elementAfter = root.childNodes[0] 211 | return elementBefore === elementAfter 212 | }) 213 | test(function() { 214 | var root = mock.document.createElement("div") 215 | m.render(root, m(".a")) 216 | var elementBefore = root.childNodes[0] 217 | m.render(root, m(".b")) 218 | var elementAfter = root.childNodes[0] 219 | return elementBefore === elementAfter 220 | }) 221 | test(function() { 222 | var root = mock.document.createElement("div") 223 | m.render(root, m("div", {id: "a"})) 224 | var elementBefore = root.childNodes[0] 225 | m.render(root, m("div", {title: "b"})) 226 | var elementAfter = root.childNodes[0] 227 | return elementBefore !== elementAfter 228 | }) 229 | test(function() { 230 | var root = mock.document.createElement("div") 231 | m.render(root, m("#a")) 232 | var elementBefore = root.childNodes[0] 233 | m.render(root, m("[title=b]")) 234 | var elementAfter = root.childNodes[0] 235 | return elementBefore !== elementAfter 236 | }) 237 | test(function() { 238 | var root = mock.document.createElement("div") 239 | m.render(root, m("#a")) 240 | var elementBefore = root.childNodes[0] 241 | m.render(root, "test") 242 | var elementAfter = root.childNodes[0] 243 | return elementBefore !== elementAfter 244 | }) 245 | test(function() { 246 | var root = mock.document.createElement("div") 247 | m.render(root, m("div", [undefined])) 248 | return root.childNodes[0].childNodes[0].nodeValue == "" 249 | }) 250 | test(function() { 251 | var root = mock.document.createElement("div") 252 | m.render(root, m("svg", [m("g")])) 253 | var g = root.childNodes[0].childNodes[0] 254 | return g.nodeName === "G" && g.namespaceURI == "http://www.w3.org/2000/svg" 255 | }) 256 | test(function() { 257 | var root = mock.document.createElement("div") 258 | m.render(root, m("svg", [m("a[href='http://google.com']")])) 259 | return root.childNodes[0].childNodes[0].nodeName === "A" 260 | }) 261 | test(function() { 262 | var root = mock.document.createElement("div") 263 | m.render(root, m("div.classname", [m("a", {href: "/first"})])) 264 | m.render(root, m("div", [m("a", {href: "/second"})])) 265 | return root.childNodes[0].childNodes.length == 1 266 | }) 267 | test(function() { 268 | var root = mock.document.createElement("div") 269 | m.render(root, m("ul", [m("li")])) 270 | m.render(root, m("ul", [m("li"), undefined])) 271 | return root.childNodes[0].childNodes[1].nodeValue == "" 272 | }) 273 | test(function() { 274 | var root = mock.document.createElement("div") 275 | m.render(root, m("ul", [m("li"), m("li")])) 276 | m.render(root, m("ul", [m("li"), undefined])) 277 | return root.childNodes[0].childNodes[1].nodeValue == "" 278 | }) 279 | test(function() { 280 | var root = mock.document.createElement("div") 281 | m.render(root, m("ul", [m("li")])) 282 | m.render(root, m("ul", [undefined])) 283 | return root.childNodes[0].childNodes[0].nodeValue == "" 284 | }) 285 | test(function() { 286 | var root = mock.document.createElement("div") 287 | m.render(root, m("ul", [m("li")])) 288 | m.render(root, m("ul", [{}])) 289 | return root.childNodes[0].childNodes.length === 0 290 | }) 291 | test(function() { 292 | var root = mock.document.createElement("div") 293 | m.render(root, m("ul", [m("li")])) 294 | m.render(root, m("ul", [{tag: "b", attrs: {}}])) 295 | return root.childNodes[0].childNodes[0].nodeName == "B" 296 | }) 297 | test(function() { 298 | var root = mock.document.createElement("div") 299 | m.render(root, m("ul", [m("li")])) 300 | m.render(root, m("ul", [{tag: new String("b"), attrs: {}}])) 301 | return root.childNodes[0].childNodes[0].nodeName == "B" 302 | }) 303 | test(function() { 304 | var root = mock.document.createElement("div") 305 | m.render(root, m("ul", [m("li", [m("a")])])) 306 | m.render(root, m("ul", [{subtree: "retain"}])) 307 | return root.childNodes[0].childNodes[0].childNodes[0].nodeName === "A" 308 | }) 309 | test(function() { 310 | //https://github.com/lhorie/mithril.js/issues/43 311 | var root = mock.document.createElement("div") 312 | m.render(root, m("a", {config: m.route}, "test")) 313 | m.render(root, m("a", {config: m.route}, "test")) 314 | return root.childNodes[0].childNodes[0].nodeValue === "test" 315 | }) 316 | test(function() { 317 | //https://github.com/lhorie/mithril.js/issues/44 (1) 318 | var root = mock.document.createElement("div") 319 | m.render(root, m("#foo", [null, m("#bar")])) 320 | m.render(root, m("#foo", ["test", m("#bar")])) 321 | return root.childNodes[0].childNodes[0].nodeValue === "test" 322 | }) 323 | test(function() { 324 | //https://github.com/lhorie/mithril.js/issues/44 (2) 325 | var root = mock.document.createElement("div") 326 | m.render(root, m("#foo", [null, m("#bar")])) 327 | m.render(root, m("#foo", [m("div"), m("#bar")])) 328 | return root.childNodes[0].childNodes[0].nodeName === "DIV" 329 | }) 330 | test(function() { 331 | //https://github.com/lhorie/mithril.js/issues/44 (3) 332 | var root = mock.document.createElement("div") 333 | m.render(root, m("#foo", ["test", m("#bar")])) 334 | m.render(root, m("#foo", [m("div"), m("#bar")])) 335 | return root.childNodes[0].childNodes[0].nodeName === "DIV" 336 | }) 337 | test(function() { 338 | //https://github.com/lhorie/mithril.js/issues/44 (4) 339 | var root = mock.document.createElement("div") 340 | m.render(root, m("#foo", [m("div"), m("#bar")])) 341 | m.render(root, m("#foo", ["test", m("#bar")])) 342 | return root.childNodes[0].childNodes[0].nodeValue === "test" 343 | }) 344 | test(function() { 345 | //https://github.com/lhorie/mithril.js/issues/44 (5) 346 | var root = mock.document.createElement("div") 347 | m.render(root, m("#foo", [m("#bar")])) 348 | m.render(root, m("#foo", [m("#bar"), [m("#baz")]])) 349 | return root.childNodes[0].childNodes[1].id === "baz" 350 | }) 351 | test(function() { 352 | //https://github.com/lhorie/mithril.js/issues/48 353 | var root = mock.document 354 | m.render(root, m("html", [m("#foo")])) 355 | var result = root.childNodes[0].childNodes[0].id === "foo" 356 | root.childNodes = [mock.document.createElement("html")] 357 | return result 358 | }) 359 | test(function() { 360 | //https://github.com/lhorie/mithril.js/issues/49 361 | var root = mock.document.createElement("div") 362 | m.render(root, m("a", "test")) 363 | m.render(root, m("a.foo", "test")) 364 | return root.childNodes[0].childNodes[0].nodeValue === "test" 365 | }) 366 | test(function() { 367 | //https://github.com/lhorie/mithril.js/issues/49 368 | var root = mock.document.createElement("div") 369 | m.render(root, m("a.foo", "test")) 370 | m.render(root, m("a", "test")) 371 | return root.childNodes[0].childNodes[0].nodeValue === "test" 372 | }) 373 | test(function() { 374 | //https://github.com/lhorie/mithril.js/issues/49 375 | var root = mock.document.createElement("div") 376 | m.render(root, m("a.foo", "test")) 377 | m.render(root, m("a", "test1")) 378 | return root.childNodes[0].childNodes[0].nodeValue === "test1" 379 | }) 380 | test(function() { 381 | //https://github.com/lhorie/mithril.js/issues/49 382 | var root = mock.document.createElement("div") 383 | m.render(root, m("a", "test")) 384 | m.render(root, m("a", "test1")) 385 | return root.childNodes[0].childNodes[0].nodeValue === "test1" 386 | }) 387 | test(function() { 388 | //https://github.com/lhorie/mithril.js/issues/50 389 | var root = mock.document.createElement("div") 390 | m.render(root, m("#foo", [[m("div", "a"), m("div", "b")], m("#bar")])) 391 | return root.childNodes[0].childNodes[1].childNodes[0].nodeValue === "b" 392 | }) 393 | test(function() { 394 | //https://github.com/lhorie/mithril.js/issues/50 395 | var root = mock.document.createElement("div") 396 | m.render(root, m("#foo", [[m("div", "a"), m("div", "b")], m("#bar")])) 397 | m.render(root, m("#foo", [[m("div", "a"), m("div", "b"), m("div", "c")], m("#bar")])) 398 | return root.childNodes[0].childNodes[2].childNodes[0].nodeValue === "c" 399 | }) 400 | test(function() { 401 | //https://github.com/lhorie/mithril.js/issues/50 402 | var root = mock.document.createElement("div") 403 | m.render(root, m("#foo", [[m("div", "a"), m("div", "b")], [m("div", "c"), m("div", "d")], m("#bar")])) 404 | return root.childNodes[0].childNodes[3].childNodes[0].nodeValue === "d" && root.childNodes[0].childNodes[4].id === "bar" 405 | }) 406 | test(function() { 407 | //https://github.com/lhorie/mithril.js/issues/50 408 | var root = mock.document.createElement("div") 409 | m.render(root, m("#foo", [[m("div", "a"), m("div", "b")], "test"])) 410 | return root.childNodes[0].childNodes[1].childNodes[0].nodeValue === "b" && root.childNodes[0].childNodes[2].nodeValue === "test" 411 | }) 412 | test(function() { 413 | //https://github.com/lhorie/mithril.js/issues/50 414 | var root = mock.document.createElement("div") 415 | m.render(root, m("#foo", [["a", "b"], "test"])) 416 | return root.childNodes[0].childNodes[1].nodeValue === "b" && root.childNodes[0].childNodes[2].nodeValue === "test" 417 | }) 418 | test(function() { 419 | //https://github.com/lhorie/mithril.js/issues/51 420 | var root = mock.document.createElement("div") 421 | m.render(root, m("main", [m("button"), m("article", [m("section"), m("nav")])])) 422 | m.render(root, m("main", [m("button"), m("article", [m("span"), m("nav")])])) 423 | return root.childNodes[0].childNodes[1].childNodes[0].nodeName === "SPAN" 424 | }) 425 | test(function() { 426 | //https://github.com/lhorie/mithril.js/issues/51 427 | var root = mock.document.createElement("div") 428 | m.render(root, m("main", [m("button"), m("article", [m("section"), m("nav")])])) 429 | m.render(root, m("main", [m("button"), m("article", ["test", m("nav")])])) 430 | return root.childNodes[0].childNodes[1].childNodes[0].nodeValue === "test" 431 | }) 432 | test(function() { 433 | //https://github.com/lhorie/mithril.js/issues/51 434 | var root = mock.document.createElement("div") 435 | m.render(root, m("main", [m("button"), m("article", [m("section"), m("nav")])])) 436 | m.render(root, m("main", [m("button"), m("article", [m.trust("test"), m("nav")])])) 437 | return root.childNodes[0].childNodes[1].childNodes[0].nodeValue === "test" 438 | }) 439 | test(function() { 440 | //https://github.com/lhorie/mithril.js/issues/55 441 | var root = mock.document.createElement("div") 442 | m.render(root, m("#a")) 443 | var elementBefore = root.childNodes[0] 444 | m.render(root, m("#b")) 445 | var elementAfter = root.childNodes[0] 446 | return elementBefore !== elementAfter 447 | }) 448 | test(function() { 449 | //https://github.com/lhorie/mithril.js/issues/56 450 | var root = mock.document.createElement("div") 451 | m.render(root, [null, "foo"]) 452 | m.render(root, ["bar"]) 453 | return root.childNodes.length == 1 454 | }) 455 | test(function() { 456 | //https://github.com/lhorie/mithril.js/issues/56 457 | var root = mock.document.createElement("div") 458 | m.render(root, m("div", "foo")) 459 | return root.childNodes.length == 1 460 | }) 461 | test(function() { 462 | var root = mock.document.createElement("div") 463 | m.render(root, m("div", [m("button"), m("ul")])) 464 | var valueBefore = root.childNodes[0].childNodes[0].nodeName 465 | m.render(root, m("div", [undefined, m("ul")])) 466 | var valueAfter = root.childNodes[0].childNodes[0].nodeValue 467 | return valueBefore === "BUTTON" && valueAfter === "" 468 | }) 469 | test(function() { 470 | var root = mock.document.createElement("div") 471 | m.render(root, m("div", [m("ul"), undefined])) 472 | var valueBefore1 = root.childNodes[0].childNodes[0].nodeName 473 | var valueBefore2 = root.childNodes[0].childNodes[1].nodeValue 474 | m.render(root, m("div", [undefined, m("ul")])) 475 | var valueAfter1 = root.childNodes[0].childNodes[0].nodeValue 476 | var valueAfter2 = root.childNodes[0].childNodes[1].nodeName 477 | return valueBefore1 === "UL" && valueAfter1 === "" && valueBefore2 === "" && valueAfter2 === "UL" 478 | }) 479 | test(function() { 480 | //https://github.com/lhorie/mithril.js/issues/79 481 | var root = mock.document.createElement("div") 482 | m.render(root, m("div", {style: {background: "red"}})) 483 | var valueBefore = root.childNodes[0].style.background 484 | m.render(root, m("div", {style: {}})) 485 | var valueAfter = root.childNodes[0].style.background 486 | return valueBefore === "red" && valueAfter === "" 487 | }) 488 | test(function() { 489 | var root = mock.document.createElement("div") 490 | m.render(root, m("div[style='background:red']")) 491 | return root.childNodes[0].style === "background:red" 492 | }) 493 | test(function() { 494 | var root = mock.document.createElement("div") 495 | m.render(root, m("div", {style: {background: "red"}})) 496 | var valueBefore = root.childNodes[0].style.background 497 | m.render(root, m("div", {})) 498 | var valueAfter = root.childNodes[0].style.background 499 | return valueBefore === "red" && valueAfter === undefined 500 | }) 501 | test(function() { 502 | //https://github.com/lhorie/mithril.js/issues/87 503 | var root = mock.document.createElement("div") 504 | m.render(root, m("div", [[m("a"), m("a")], m("button")])) 505 | m.render(root, m("div", [[m("a")], m("button")])) 506 | return root.childNodes[0].childNodes.length == 2 && root.childNodes[0].childNodes[1].nodeName == "BUTTON" 507 | }) 508 | test(function() { 509 | //https://github.com/lhorie/mithril.js/issues/87 510 | var root = mock.document.createElement("div") 511 | m.render(root, m("div", [m("a"), m("b"), m("button")])) 512 | m.render(root, m("div", [m("a"), m("button")])) 513 | return root.childNodes[0].childNodes.length == 2 && root.childNodes[0].childNodes[1].nodeName == "BUTTON" 514 | }) 515 | test(function() { 516 | //https://github.com/lhorie/mithril.js/issues/99 517 | var root = mock.document.createElement("div") 518 | m.render(root, m("div", [m("img"), m("h1")])) 519 | m.render(root, m("div", [m("a")])) 520 | return root.childNodes[0].childNodes.length == 1 && root.childNodes[0].childNodes[0].nodeName == "A" 521 | }) 522 | test(function() { 523 | //https://github.com/lhorie/mithril.js/issues/120 524 | var root = mock.document.createElement("div") 525 | m.render(root, m("div", ["a", "b", "c", "d"])) 526 | m.render(root, m("div", [["d", "e"]])) 527 | var children = root.childNodes[0].childNodes 528 | return children.length == 2 && children[0].nodeValue == "d" && children[1].nodeValue == "e" 529 | }) 530 | test(function() { 531 | //https://github.com/lhorie/mithril.js/issues/120 532 | var root = mock.document.createElement("div") 533 | m.render(root, m("div", [["a", "b", "c", "d"]])) 534 | m.render(root, m("div", ["d", "e"])) 535 | var children = root.childNodes[0].childNodes 536 | return children.length == 2 && children[0].nodeValue == "d" && children[1].nodeValue == "e" 537 | }) 538 | test(function() { 539 | //https://github.com/lhorie/mithril.js/issues/120 540 | var root = mock.document.createElement("div") 541 | m.render(root, m("div", ["x", [["a"], "b", "c", "d"]])) 542 | m.render(root, m("div", ["d", ["e"]])) 543 | var children = root.childNodes[0].childNodes 544 | return children.length == 2 && children[0].nodeValue == "d" && children[1].nodeValue == "e" 545 | }) 546 | test(function() { 547 | //https://github.com/lhorie/mithril.js/issues/120 548 | var root = mock.document.createElement("div") 549 | m.render(root, m("div", ["b"])) 550 | m.render(root, m("div", [["e"]])) 551 | var children = root.childNodes[0].childNodes 552 | return children.length == 1 && children[0].nodeValue == "e" 553 | }) 554 | test(function() { 555 | //https://github.com/lhorie/mithril.js/issues/120 556 | var root = mock.document.createElement("div") 557 | m.render(root, m("div", ["a", ["b"]])) 558 | m.render(root, m("div", ["d", [["e"]]])) 559 | var children = root.childNodes[0].childNodes 560 | return children.length == 2 && children[0].nodeValue == "d" && children[1].nodeValue == "e" 561 | }) 562 | test(function() { 563 | //https://github.com/lhorie/mithril.js/issues/120 564 | var root = mock.document.createElement("div") 565 | m.render(root, m("div", ["a", [["b"]]])) 566 | m.render(root, m("div", ["d", ["e"]])) 567 | var children = root.childNodes[0].childNodes 568 | return children.length == 2 && children[0].nodeValue == "d" && children[1].nodeValue == "e" 569 | }) 570 | test(function() { 571 | //https://github.com/lhorie/mithril.js/issues/120 572 | var root = mock.document.createElement("div") 573 | m.render(root, m("div", ["a", [["b"], "c"]])) 574 | m.render(root, m("div", ["d", [[["e"]], "x"]])) 575 | var children = root.childNodes[0].childNodes 576 | return children.length == 3 && children[0].nodeValue == "d" && children[1].nodeValue == "e" 577 | }) 578 | test(function() { 579 | var root = mock.document.createElement("div") 580 | 581 | var success = false 582 | m.render(root, m("div", {config: function(elem, isInitialized, ctx) {ctx.data = 1}})) 583 | m.render(root, m("div", {config: function(elem, isInitialized, ctx) {success = ctx.data === 1}})) 584 | return success 585 | }) 586 | test(function() { 587 | var root = mock.document.createElement("div") 588 | 589 | var index = 0; 590 | var success = true; 591 | var statefulConfig = function(elem, isInitialized, ctx) {ctx.data = index++} 592 | var node = m("div", {config: statefulConfig}); 593 | m.render(root, [node, node]); 594 | 595 | index = 0; 596 | var checkConfig = function(elem, isInitialized, ctx) { 597 | success = success && (ctx.data === index++) 598 | } 599 | node = m("div", {config: checkConfig}); 600 | m.render(root, [node, node]); 601 | return success; 602 | }) 603 | test(function() { 604 | var root = mock.document.createElement("div") 605 | var parent 606 | m.render(root, m("div", m("a", { 607 | config: function(el) {parent = el.parentNode.parentNode} 608 | }))); 609 | return parent === root 610 | }) 611 | test(function() { 612 | var root = mock.document.createElement("div") 613 | var count = 0 614 | m.render(root, m("div", m("a", { 615 | config: function(el) { 616 | var island = mock.document.createElement("div") 617 | count++ 618 | if (count > 2) throw "too much recursion..." 619 | m.render(island, m("div")) 620 | } 621 | }))); 622 | return count == 1 623 | }) 624 | test(function() { 625 | //https://github.com/lhorie/mithril.js/issues/129 626 | var root = mock.document.createElement("div") 627 | m.render(root, m("div", [["foo", "bar"], ["foo", "bar"], ["foo", "bar"]])); 628 | m.render(root, m("div", ["asdf", "asdf2", "asdf3"])); 629 | return true 630 | }) 631 | test(function() { 632 | //https://github.com/lhorie/mithril.js/issues/98 633 | //insert at beginning 634 | var root = mock.document.createElement("div") 635 | m.render(root, [m("a", {key: 1}, 1), m("a", {key: 2}, 2), m("a", {key: 3}, 3)]) 636 | var firstBefore = root.childNodes[0] 637 | m.render(root, [m("a", {key: 4}, 4), m("a", {key: 1}, 1), m("a", {key: 2}, 2), m("a", {key: 3}, 3)]) 638 | var firstAfter = root.childNodes[1] 639 | return firstBefore == firstAfter && root.childNodes[0].childNodes[0].nodeValue == "4" && root.childNodes.length == 4 640 | }) 641 | test(function() { 642 | //https://github.com/lhorie/mithril.js/issues/98 643 | var root = mock.document.createElement("div") 644 | m.render(root, [m("a", {key: 1}, 1), m("a", {key: 2}, 2), m("a", {key: 3}, 3)]) 645 | var firstBefore = root.childNodes[0] 646 | m.render(root, [m("a", {key: 4}, 4), m("a", {key: 1}, 1), m("a", {key: 2}, 2)]) 647 | var firstAfter = root.childNodes[1] 648 | return firstBefore == firstAfter && root.childNodes[0].childNodes[0].nodeValue == 4 && root.childNodes.length == 3 649 | }) 650 | test(function() { 651 | //https://github.com/lhorie/mithril.js/issues/98 652 | var root = mock.document.createElement("div") 653 | m.render(root, [m("a", {key: 1}, 1), m("a", {key: 2}, 2), m("a", {key: 3}, 3)]) 654 | var firstBefore = root.childNodes[1] 655 | m.render(root, [m("a", {key: 2}, 2), m("a", {key: 3}, 3), m("a", {key: 4}, 4)]) 656 | var firstAfter = root.childNodes[0] 657 | return firstBefore == firstAfter && root.childNodes[0].childNodes[0].nodeValue === "2" && root.childNodes.length === 3 658 | }) 659 | test(function() { 660 | //https://github.com/lhorie/mithril.js/issues/98 661 | var root = mock.document.createElement("div") 662 | m.render(root, [m("a", {key: 1}, 1), m("a", {key: 2}, 2), m("a", {key: 3}, 3), m("a", {key: 4}, 4), m("a", {key: 5}, 5)]) 663 | var firstBefore = root.childNodes[0] 664 | var secondBefore = root.childNodes[1] 665 | var fourthBefore = root.childNodes[3] 666 | m.render(root, [m("a", {key: 4}, 4), m("a", {key: 10}, 10), m("a", {key: 1}, 1), m("a", {key: 2}, 2)]) 667 | var firstAfter = root.childNodes[2] 668 | var secondAfter = root.childNodes[3] 669 | var fourthAfter = root.childNodes[0] 670 | return firstBefore === firstAfter && secondBefore === secondAfter && fourthBefore === fourthAfter && root.childNodes[1].childNodes[0].nodeValue == "10" && root.childNodes.length === 4 671 | }) 672 | test(function() { 673 | //https://github.com/lhorie/mithril.js/issues/98 674 | var root = mock.document.createElement("div") 675 | m.render(root, [m("a", {key: 1}, 1), m("a", {key: 2}, 2), m("a", {key: 3}, 3), m("a", {key: 4}, 4), m("a", {key: 5}, 5)]) 676 | var firstBefore = root.childNodes[0] 677 | var secondBefore = root.childNodes[1] 678 | var fourthBefore = root.childNodes[3] 679 | m.render(root, [m("a", {key: 4}, 4), m("a", {key: 10}, 10), m("a", {key: 2}, 2), m("a", {key: 1}, 1), m("a", {key: 6}, 6), m("a", {key: 7}, 7)]) 680 | var firstAfter = root.childNodes[3] 681 | var secondAfter = root.childNodes[2] 682 | var fourthAfter = root.childNodes[0] 683 | return firstBefore === firstAfter && secondBefore === secondAfter && fourthBefore === fourthAfter && root.childNodes[1].childNodes[0].nodeValue == "10" && root.childNodes[4].childNodes[0].nodeValue == "6" && root.childNodes[5].childNodes[0].nodeValue == "7" && root.childNodes.length === 6 684 | }) 685 | test(function() { 686 | //https://github.com/lhorie/mithril.js/issues/149 687 | var root = mock.document.createElement("div") 688 | m.render(root, [m("a", {key: 1}), m("a", {key: 2}), m("a"), m("a", {key: 4}), m("a", {key: 5})]) 689 | var firstBefore = root.childNodes[0] 690 | var secondBefore = root.childNodes[1] 691 | var thirdBefore = root.childNodes[2] 692 | var fourthBefore = root.childNodes[3] 693 | var fifthBefore = root.childNodes[4] 694 | m.render(root, [m("a", {key: 4}), m("a", {key: 5}), m("a"), m("a", {key: 1}), m("a", {key: 2})]) 695 | var firstAfter = root.childNodes[3] 696 | var secondAfter = root.childNodes[4] 697 | var thirdAfter = root.childNodes[2] 698 | var fourthAfter = root.childNodes[0] 699 | var fifthAfter = root.childNodes[1] 700 | return firstBefore === firstAfter && secondBefore === secondAfter && thirdBefore === thirdAfter && fourthBefore === fourthAfter && fifthBefore === fifthAfter 701 | }) 702 | test(function() { 703 | //https://github.com/lhorie/mithril.js/issues/246 704 | //insert at beginning with non-keyed in the middle 705 | var root = mock.document.createElement("div") 706 | m.render(root, [m("a", {key: 1}, 1)]) 707 | var firstBefore = root.childNodes[0] 708 | m.render(root, [m("a", {key: 2}, 2), m("br"), m("a", {key: 1}, 1)]) 709 | var firstAfter = root.childNodes[2] 710 | return firstBefore == firstAfter && root.childNodes[0].childNodes[0].nodeValue == 2 && root.childNodes.length == 3 711 | }) 712 | test(function() { 713 | //https://github.com/lhorie/mithril.js/issues/134 714 | var root = mock.document.createElement("div") 715 | m.render(root, m("div", {contenteditable: true}, "test")) 716 | mock.document.activeElement = root.childNodes[0] 717 | m.render(root, m("div", {contenteditable: true}, "test1")) 718 | m.render(root, m("div", {contenteditable: false}, "test2")) 719 | return root.childNodes[0].childNodes[0].nodeValue === "test2" 720 | }) 721 | test(function() { 722 | //https://github.com/lhorie/mithril.js/issues/136 723 | var root = mock.document.createElement("div") 724 | m.render(root, m("textarea", ["test"])) 725 | m.render(root, m("textarea", ["test1"])) 726 | return root.childNodes[0].value === "test1" 727 | }) 728 | test(function() { 729 | var root = mock.document.createElement("div") 730 | var unloaded = 0 731 | m.render(root, [ 732 | m("div", { 733 | key: 1, 734 | config: function(el, init, ctx) { 735 | ctx.onunload = function() { 736 | unloaded++ 737 | } 738 | } 739 | }) 740 | ]) 741 | m.render(root, [ 742 | m("div", {key: 2}), 743 | m("div", { 744 | key: 1, 745 | config: function(el, init, ctx) { 746 | ctx.onunload = function() { 747 | unloaded++ 748 | } 749 | } 750 | }) 751 | ]) 752 | return unloaded == 0 753 | }) 754 | test(function() { 755 | var root = mock.document.createElement("div") 756 | var unloadedParent = 0 757 | var unloadedChild = 0 758 | var configParent = function(el, init, ctx) { 759 | ctx.onunload = function() { 760 | unloadedParent++ 761 | } 762 | } 763 | var configChild = function(el, init, ctx) { 764 | ctx.onunload = function() { 765 | unloadedChild++ 766 | } 767 | } 768 | var unloaded = 0 769 | m.render(root, m("div", {config: configParent}, m("a", {config: configChild}))) 770 | m.render(root, m("main", {config: configParent}, m("a", {config: configChild}))) 771 | return unloadedParent === 1 && unloadedChild === 0 772 | }) 773 | test(function() { 774 | var root = mock.document.createElement("div") 775 | var unloadedParent = 0 776 | var unloadedChild = 0 777 | var configParent = function(el, init, ctx) { 778 | ctx.onunload = function() { 779 | unloadedParent++ 780 | } 781 | } 782 | var configChild = function(el, init, ctx) { 783 | ctx.onunload = function() { 784 | unloadedChild++ 785 | } 786 | } 787 | var unloaded = 0 788 | m.render(root, m("div", {config: configParent}, m("a", {config: configChild}))) 789 | m.render(root, m("main", {config: configParent}, m("b", {config: configChild}))) 790 | return unloadedParent === 1 && unloadedChild === 1 791 | }) 792 | test(function() { 793 | //https://github.com/lhorie/mithril.js/issues/150 794 | var root = mock.document.createElement("div") 795 | m.render(root, [m("a"), m("div")]) 796 | m.render(root, [[], m("div")]) 797 | return root.childNodes.length == 1 && root.childNodes[0].nodeName == "DIV" 798 | }) 799 | test(function() { 800 | //https://github.com/lhorie/mithril.js/issues/156 801 | var root = mock.document.createElement("div") 802 | m.render(root, m("div", [ 803 | ["a", "b", "c", "d"].map(function() { 804 | return [m("div"), " "] 805 | }), 806 | m("span") 807 | ])) 808 | return root.childNodes[0].childNodes[8].nodeName == "SPAN" 809 | }) 810 | test(function() { 811 | //https://github.com/lhorie/mithril.js/issues/157 812 | var root = mock.document.createElement("div") 813 | m.render(root, m("ul", [m("li", {key: 0}, 0), m("li", {key: 2}, 2), m("li", {key: 4}, 4)])) 814 | m.render(root, m("ul", [m("li", {key: 0}, 0), m("li", {key: 1}, 1), m("li", {key: 2}, 2), m("li", {key: 3}, 3), m("li", {key: 4}, 4), m("li", {key: 5}, 5)])) 815 | return root.childNodes[0].childNodes.map(function(n) {return n.childNodes[0].nodeValue}).join("") == "012345" 816 | }) 817 | test(function() { 818 | //https://github.com/lhorie/mithril.js/issues/157 819 | var root = mock.document.createElement("div") 820 | m.render(root, m("input", {value: "a"})) 821 | m.render(root, m("input", {value: "aa"})) 822 | return root.childNodes[0].childNodes.length == 0 823 | }) 824 | test(function() { 825 | //https://github.com/lhorie/mithril.js/issues/157 826 | var root = mock.document.createElement("div") 827 | m.render(root, m("br", {"class": "a"})) 828 | m.render(root, m("br", {"class": "aa"})) 829 | return root.childNodes[0].childNodes.length == 0 830 | }) 831 | test(function() { 832 | //https://github.com/lhorie/mithril.js/issues/194 833 | var root = mock.document.createElement("div") 834 | m.render(root, m("ul", [m("li", {key: 0}, 0), m("li", {key: 1}, 1), m("li", {key: 2}, 2), m("li", {key: 3}, 3), m("li", {key: 4}, 4), m("li", {key: 5}, 5)])) 835 | m.render(root, m("ul", [m("li", {key: 0}, 0), m("li", {key: 1}, 1), m("li", {key: 2}, 2), m("li", {key: 4}, 4), m("li", {key: 5}, 5)])) 836 | return root.childNodes[0].childNodes.map(function(n) {return n.childNodes[0].nodeValue}).join("") == "01245" 837 | }) 838 | test(function() { 839 | //https://github.com/lhorie/mithril.js/issues/194 840 | var root = mock.document.createElement("div") 841 | m.render(root, m("ul", [m("li", {key: 0}, 0), m("li", {key: 1}, 1), m("li", {key: 2}, 2), m("li", {key: 3}, 3), m("li", {key: 4}, 4), m("li", {key: 5}, 5)])) 842 | m.render(root, m("ul", [m("li", {key: 1}, 1), m("li", {key: 2}, 2), m("li", {key: 3}, 3), m("li", {key: 4}, 4), m("li", {key: 5}, 5), m("li", {key: 6}, 6)])) 843 | m.render(root, m("ul", [m("li", {key: 12}, 12), m("li", {key: 13}, 13), m("li", {key: 14}, 14), m("li", {key: 15}, 15), m("li", {key: 16}, 16), m("li", {key: 17}, 17)])) 844 | return root.childNodes[0].childNodes.map(function(n) {return n.childNodes[0].nodeValue}).join(",") == "12,13,14,15,16,17" 845 | }) 846 | test(function() { 847 | //https://github.com/lhorie/mithril.js/issues/206 848 | var root = mock.document.createElement("div") 849 | m.render(root, m("div", undefined)) 850 | m.render(root, m("div", [m("div")])) 851 | return root.childNodes[0].childNodes.length == 1 852 | }) 853 | test(function() { 854 | //https://github.com/lhorie/mithril.js/issues/206 855 | var root = mock.document.createElement("div") 856 | m.render(root, m("div", null)) 857 | m.render(root, m("div", [m("div")])) 858 | return root.childNodes[0].childNodes.length == 1 859 | }) 860 | test(function() { 861 | //https://github.com/lhorie/mithril.js/issues/200 862 | var root = mock.document.createElement("div") 863 | 864 | var unloaded1 = false 865 | function unloadable1(element, isInit, context) { 866 | context.onunload = function() { 867 | unloaded1 = true 868 | } 869 | } 870 | m.render(root, [ m("div", {config: unloadable1}) ]) 871 | m.render(root, [ ]) 872 | 873 | var unloaded2 = false 874 | function unloadable2(element, isInit, context) { 875 | context.onunload = function() { 876 | unloaded2 = true 877 | } 878 | } 879 | m.render(root, [ m("div", {config: unloadable2}) ]) 880 | m.render(root, [ ]) 881 | 882 | return unloaded1 === true && unloaded2 === true 883 | }) 884 | test(function() { 885 | var root = mock.document.createElement("div") 886 | m.render(root, [m("div.blue")]) 887 | m.render(root, [m("div.green", [m("div")]), m("div.blue")]) 888 | return root.childNodes.length == 2 889 | }) 890 | test(function() { 891 | //https://github.com/lhorie/mithril.js/issues/277 892 | var root = mock.document.createElement("div") 893 | function Field() { 894 | this.tag = "div"; 895 | this.attrs = {}; 896 | this.children = "hello"; 897 | } 898 | m.render(root, new Field()) 899 | return root.childNodes.length == 1 900 | }) 901 | test(function() { 902 | var root = mock.document.createElement("div") 903 | m.render(root, {foo: 123}) 904 | return root.childNodes.length == 0 905 | }) 906 | test(function() { 907 | //https://github.com/lhorie/mithril.js/issues/299 908 | var root = mock.document.createElement("div") 909 | m.render(root, m("div", [m("div", {key: 1}, 1), m("div", {key: 2}, 2), m("div", {key: 3}, 3), m("div", {key: 4}, 4), m("div", {key: 5}, 5), null, null, null, null, null, null, null, null, null, null])) 910 | m.render(root, m("div", [null, null, m("div", {key: 3}, 3), null, null, m("div", {key: 6}, 6), null, null, m("div", {key: 9}, 9), null, null, m("div", {key: 12}, 12), null, null, m("div", {key: 15}, 15)])) 911 | m.render(root, m("div", [m("div", {key: 1}, 1), m("div", {key: 2}, 2), m("div", {key: 3}, 3), m("div", {key: 4}, 4), m("div", {key: 5}, 5), null, null, null, null, null, null, null, null, null, null])) 912 | return root.childNodes[0].childNodes.map(function(c) {return c.childNodes ? c.childNodes[0].nodeValue: c.nodeValue}).slice(0, 5).join("") == "12345" 913 | }) 914 | test(function() { 915 | //https://github.com/lhorie/mithril.js/issues/377 916 | var root = mock.document.createElement("div") 917 | m.render(root, m("div", [m("div", 1), m("div", 2), [m("div", {key: 3}, 3), m("div", {key: 4}, 4), m("div", {key:5}, 5)], [m("div", {key: 6}, 6)]])) 918 | m.render(root, m("div", [m("div", 1), null, [m("div", {key: 3}, 3), m("div", {key: 4}, 4), m("div", {key:5}, 5)], [m("div", {key: 6}, 6)]])) 919 | return root.childNodes[0].childNodes.map(function(c) {return c.childNodes ? c.childNodes[0].nodeValue: c.nodeValue}).join("") == "13456" 920 | }) 921 | test(function() { 922 | var root = mock.document.createElement("div") 923 | m.render(root, m("div", [console.log()])) //don't throw in Firefox 924 | return true 925 | }) 926 | test(function() { 927 | var root = mock.document.createElement("div") 928 | m.render(root, [ 929 | m("#div-1", {key: 1}), 930 | m("#div-2", {key: 2}), 931 | m("#div-3", {key: 3}) 932 | ]) 933 | root.appendChild(root.childNodes[1]) 934 | m.render(root, [ 935 | m("#div-1", {key: 1}), 936 | m("#div-3", {key: 3}), 937 | m("#div-2", {key: 2}) 938 | ]) 939 | return root.childNodes.map(function(node) {return node.id}).join() == "div-1,div-3,div-2" 940 | }) 941 | test(function() { 942 | var root = mock.document.createElement("div") 943 | m.render(root, m("div", function() {})) 944 | return root.childNodes[0].childNodes.length == 0 945 | }) 946 | test(function() { 947 | var root = mock.document.createElement("div") 948 | m.render(root, m("div", "foo", m("a"))) 949 | m.render(root, m("div", "test")) 950 | return root.childNodes[0].childNodes.length == 1 951 | }) 952 | test(function() { 953 | //if an element is preceded by a conditional, it should not lose its identity 954 | var root = mock.document.createElement("div") 955 | m.render(root, m("div", [m("a"), m("input[autofocus]")])) 956 | var before = root.childNodes[0].childNodes[1] 957 | m.render(root, m("div", [undefined, m("input[autofocus]")])) 958 | var after = root.childNodes[0].childNodes[1] 959 | return before === after 960 | }) 961 | test(function() { 962 | //unkeyed element should maintain identity if mixed w/ keyed elements and identity can be inferred 963 | var root = mock.document.createElement("div") 964 | m.render(root, m("div", [m("a", {key: 1}), m("a", {key: 2}), m("a", {key: 3}), m("i")])) 965 | var before = root.childNodes[0].childNodes[3] 966 | m.render(root, m("div", [m("b", {key: 3}), m("b", {key: 4}), m("i"), m("b", {key: 1})])) 967 | var after = root.childNodes[0].childNodes[2] 968 | return before === after 969 | }) 970 | test(function() { 971 | //unkeyed element should maintain identity if mixed w/ keyed elements and text nodes and identity can be inferred 972 | var root = mock.document.createElement("div") 973 | m.render(root, m("div", [m("a", {key: 1}), m("a", {key: 2}), "foo", m("a", {key: 3}), m("i")])) 974 | var before = root.childNodes[0].childNodes[4] 975 | m.render(root, m("div", [m("a", {key: 3}), m("a", {key: 4}), "bar", m("i"), m("a", {key: 1})])) 976 | var after = root.childNodes[0].childNodes[3] 977 | return before === after 978 | }) 979 | test(function() { 980 | var root = mock.document.createElement("div") 981 | m.render(root, m("div", [m("a", {key: 1}), m("a", {key: 2}), null, m("a", {key: 3}), m("i")])) 982 | var before = root.childNodes[0].childNodes[4] 983 | m.render(root, m("div", [m("a", {key: 3}), m("a", {key: 4}), null, m("i"), m("a", {key: 1})])) 984 | var after = root.childNodes[0].childNodes[3] 985 | return before === after 986 | }) 987 | test(function() { 988 | var root = mock.document.createElement("div") 989 | m.render(root, m("div", [m("a", {key: 1}), m("a", {key: 2}), undefined, m("a", {key: 3}), m("i")])) 990 | var before = root.childNodes[0].childNodes[4] 991 | m.render(root, m("div", [m("a", {key: 3}), m("a", {key: 4}), undefined, m("i"), m("a", {key: 1})])) 992 | var after = root.childNodes[0].childNodes[3] 993 | return before === after 994 | }) 995 | test(function() { 996 | var root = mock.document.createElement("div") 997 | m.render(root, m("div", [m("a", {key: 1}), m("a", {key: 2}), m.trust("a"), m("a", {key: 3}), m("i")])) 998 | var before = root.childNodes[0].childNodes[4] 999 | m.render(root, m("div", [m("a", {key: 3}), m("a", {key: 4}), m.trust("a"), m("i"), m("a", {key: 1})])) 1000 | var after = root.childNodes[0].childNodes[3] 1001 | return before === after 1002 | }) 1003 | //end m.render 1004 | 1005 | //m.redraw 1006 | test(function() { 1007 | mock.requestAnimationFrame.$resolve() //setup 1008 | var controller 1009 | var root = mock.document.createElement("div") 1010 | m.module(root, { 1011 | controller: function() {controller = this}, 1012 | view: function(ctrl) {return ctrl.value} 1013 | }) 1014 | mock.requestAnimationFrame.$resolve() 1015 | var valueBefore = root.childNodes[0].nodeValue 1016 | controller.value = "foo" 1017 | m.redraw() 1018 | mock.requestAnimationFrame.$resolve() 1019 | return valueBefore === "" && root.childNodes[0].nodeValue === "foo" 1020 | }) 1021 | test(function() { 1022 | mock.requestAnimationFrame.$resolve() //setup 1023 | var count = 0 1024 | var root = mock.document.createElement("div") 1025 | m.module(root, { 1026 | controller: function() {}, 1027 | view: function(ctrl) { 1028 | count++ 1029 | } 1030 | }) 1031 | mock.requestAnimationFrame.$resolve() //teardown 1032 | m.redraw() //should run synchronously 1033 | 1034 | m.redraw() //rest should run asynchronously since they're spamming 1035 | m.redraw() 1036 | m.redraw() 1037 | mock.requestAnimationFrame.$resolve() //teardown 1038 | return count === 3 1039 | }) 1040 | test(function() { 1041 | mock.requestAnimationFrame.$resolve() //setup 1042 | var count = 0 1043 | var root = mock.document.createElement("div") 1044 | m.module(root, { 1045 | controller: function() {}, 1046 | view: function(ctrl) { 1047 | count++ 1048 | } 1049 | }) 1050 | mock.requestAnimationFrame.$resolve() //teardown 1051 | m.redraw(true) //should run synchronously 1052 | 1053 | m.redraw(true) //forced to run synchronously 1054 | m.redraw(true) 1055 | m.redraw(true) 1056 | mock.requestAnimationFrame.$resolve() //teardown 1057 | return count === 5 1058 | }) 1059 | 1060 | //m.route 1061 | test(function() { 1062 | mock.requestAnimationFrame.$resolve() //setup 1063 | mock.location.search = "?" 1064 | 1065 | var root = mock.document.createElement("div") 1066 | m.route.mode = "search" 1067 | m.route(root, "/test1", { 1068 | "/test1": {controller: function() {}, view: function() {return "foo"}} 1069 | }) 1070 | mock.requestAnimationFrame.$resolve() //teardown 1071 | return mock.location.search == "?/test1" && root.childNodes[0].nodeValue === "foo" 1072 | }) 1073 | test(function() { 1074 | mock.requestAnimationFrame.$resolve() //setup 1075 | mock.location.pathname = "/" 1076 | 1077 | var root = mock.document.createElement("div") 1078 | m.route.mode = "pathname" 1079 | m.route(root, "/test2", { 1080 | "/test2": { 1081 | controller: function() {}, 1082 | view: function() { 1083 | return [ 1084 | "foo", 1085 | m("a", { href: "/test2", config: m.route }, "Test2") 1086 | ] 1087 | } 1088 | } 1089 | }) 1090 | mock.requestAnimationFrame.$resolve() //teardown 1091 | return mock.location.pathname == "/test2" && 1092 | root.childNodes[0].nodeValue === "foo" && 1093 | root.childNodes[1].href == "/test2" 1094 | }) 1095 | test(function() { 1096 | mock.requestAnimationFrame.$resolve() //setup 1097 | mock.location.hash = "#" 1098 | 1099 | var root = mock.document.createElement("div") 1100 | m.route.mode = "hash" 1101 | m.route(root, "/test3", { 1102 | "/test3": {controller: function() {}, view: function() {return "foo"}} 1103 | }) 1104 | mock.requestAnimationFrame.$resolve() //teardown 1105 | return mock.location.hash == "#/test3" && root.childNodes[0].nodeValue === "foo" 1106 | }) 1107 | test(function() { 1108 | mock.requestAnimationFrame.$resolve() //setup 1109 | mock.location.search = "?" 1110 | 1111 | var root = mock.document.createElement("div") 1112 | m.route.mode = "search" 1113 | m.route(root, "/test4/foo", { 1114 | "/test4/:test": {controller: function() {}, view: function() {return m.route.param("test")}} 1115 | }) 1116 | mock.requestAnimationFrame.$resolve() //teardown 1117 | return mock.location.search == "?/test4/foo" && root.childNodes[0].nodeValue === "foo" 1118 | }) 1119 | test(function() { 1120 | mock.requestAnimationFrame.$resolve() //setup 1121 | mock.location.search = "?" 1122 | 1123 | var module = {controller: function() {}, view: function() {return m.route.param("test")}} 1124 | 1125 | var root = mock.document.createElement("div") 1126 | m.route.mode = "search" 1127 | m.route(root, "/test5/foo", { 1128 | "/": module, 1129 | "/test5/:test": module 1130 | }) 1131 | var paramValueBefore = m.route.param("test") 1132 | mock.requestAnimationFrame.$resolve() 1133 | m.route("/") 1134 | var paramValueAfter = m.route.param("test") 1135 | mock.requestAnimationFrame.$resolve() //teardown 1136 | return mock.location.search == "?/" && paramValueBefore === "foo" && paramValueAfter === undefined 1137 | }) 1138 | test(function() { 1139 | mock.requestAnimationFrame.$resolve() //setup 1140 | mock.location.search = "?" 1141 | 1142 | var module = {controller: function() {}, view: function() {return m.route.param("a1")}} 1143 | 1144 | var root = mock.document.createElement("div") 1145 | m.route.mode = "search" 1146 | m.route(root, "/test6/foo", { 1147 | "/": module, 1148 | "/test6/:a1": module 1149 | }) 1150 | var paramValueBefore = m.route.param("a1") 1151 | mock.requestAnimationFrame.$resolve() 1152 | m.route("/") 1153 | var paramValueAfter = m.route.param("a1") 1154 | mock.requestAnimationFrame.$resolve() //teardown 1155 | return mock.location.search == "?/" && paramValueBefore === "foo" && paramValueAfter === undefined 1156 | }) 1157 | test(function() { 1158 | //https://github.com/lhorie/mithril.js/issues/61 1159 | mock.requestAnimationFrame.$resolve() //setup 1160 | mock.location.search = "?" 1161 | 1162 | var module = {controller: function() {}, view: function() {return m.route.param("a1")}} 1163 | 1164 | var root = mock.document.createElement("div") 1165 | m.route.mode = "search" 1166 | m.route(root, "/test7/foo", { 1167 | "/": module, 1168 | "/test7/:a1": module 1169 | }) 1170 | var routeValueBefore = m.route() 1171 | mock.requestAnimationFrame.$resolve() 1172 | m.route("/") 1173 | var routeValueAfter = m.route() 1174 | mock.requestAnimationFrame.$resolve() //teardown 1175 | return routeValueBefore === "/test7/foo" && routeValueAfter === "/" 1176 | }) 1177 | test(function() { 1178 | mock.requestAnimationFrame.$resolve() //setup 1179 | mock.location.search = "?" 1180 | 1181 | var root = mock.document.createElement("div") 1182 | m.route.mode = "search" 1183 | m.route(root, "/test8/foo/SEP/bar/baz", { 1184 | "/test8/:test/SEP/:path...": { 1185 | controller: function() {}, 1186 | view: function() { 1187 | return m.route.param("test") + "_" + m.route.param("path") 1188 | } 1189 | } 1190 | }) 1191 | mock.requestAnimationFrame.$resolve() //teardown 1192 | return mock.location.search == "?/test8/foo/SEP/bar/baz" && root.childNodes[0].nodeValue === "foo_bar/baz" 1193 | }) 1194 | test(function() { 1195 | mock.requestAnimationFrame.$resolve() //setup 1196 | mock.location.search = "?" 1197 | 1198 | var root = mock.document.createElement("div") 1199 | m.route.mode = "search" 1200 | m.route(root, "/test9/foo/bar/SEP/baz", { 1201 | "/test9/:test.../SEP/:path": { 1202 | controller: function() {}, 1203 | view: function() { 1204 | return m.route.param("test") + "_" + m.route.param("path") 1205 | } 1206 | } 1207 | }) 1208 | mock.requestAnimationFrame.$resolve() //teardown 1209 | return mock.location.search == "?/test9/foo/bar/SEP/baz" && root.childNodes[0].nodeValue === "foo/bar_baz" 1210 | }) 1211 | test(function() { 1212 | mock.requestAnimationFrame.$resolve() //setup 1213 | mock.location.search = "?" 1214 | 1215 | var root = mock.document.createElement("div") 1216 | m.route.mode = "search" 1217 | m.route(root, "/test10/foo%20bar", { 1218 | "/test10/:test": { 1219 | controller: function() {}, 1220 | view: function() { 1221 | return m.route.param("test") 1222 | } 1223 | } 1224 | }) 1225 | mock.requestAnimationFrame.$resolve() //teardown 1226 | return root.childNodes[0].nodeValue === "foo bar" 1227 | }) 1228 | test(function() { 1229 | mock.requestAnimationFrame.$resolve() //setup 1230 | mock.location.search = "?" 1231 | 1232 | var root = mock.document.createElement("div") 1233 | m.route.mode = "search" 1234 | m.route(root, "/", { 1235 | "/": {controller: function() {}, view: function() {return "foo"}}, 1236 | "/test11": {controller: function() {}, view: function() {return "bar"}} 1237 | }) 1238 | mock.requestAnimationFrame.$resolve() 1239 | m.route("/test11/") 1240 | mock.requestAnimationFrame.$resolve() //teardown 1241 | return mock.location.search == "?/test11/" && root.childNodes[0].nodeValue === "bar" 1242 | }) 1243 | test(function() { 1244 | mock.requestAnimationFrame.$resolve() //setup 1245 | mock.location.search = "?" 1246 | 1247 | var root = mock.document.createElement("div") 1248 | m.route.mode = "search" 1249 | m.route(root, "/", { 1250 | "/": {controller: function() {}, view: function() {}}, 1251 | "/test12": {controller: function() {}, view: function() {}} 1252 | }) 1253 | mock.requestAnimationFrame.$resolve() 1254 | m.route("/test12?a=foo&b=bar") 1255 | mock.requestAnimationFrame.$resolve() //teardown 1256 | return mock.location.search == "?/test12?a=foo&b=bar" && m.route.param("a") == "foo" && m.route.param("b") == "bar" 1257 | }) 1258 | test(function() { 1259 | mock.requestAnimationFrame.$resolve() //setup 1260 | mock.location.search = "?" 1261 | 1262 | var root = mock.document.createElement("div") 1263 | m.route.mode = "search" 1264 | m.route(root, "/", { 1265 | "/": {controller: function() {}, view: function() {return "bar"}}, 1266 | "/test13/:test": {controller: function() {}, view: function() {return m.route.param("test")}} 1267 | }) 1268 | mock.requestAnimationFrame.$resolve() 1269 | m.route("/test13/foo?test=bar") 1270 | mock.requestAnimationFrame.$resolve() //teardown 1271 | return mock.location.search == "?/test13/foo?test=bar" && root.childNodes[0].nodeValue === "foo" 1272 | }) 1273 | test(function() { 1274 | mock.requestAnimationFrame.$resolve() //setup 1275 | mock.location.search = "?" 1276 | 1277 | var root = mock.document.createElement("div") 1278 | m.route.mode = "search" 1279 | m.route(root, "/", { 1280 | "/": {controller: function() {}, view: function() {return "bar"}}, 1281 | "/test14": {controller: function() {}, view: function() {return "foo"}} 1282 | }) 1283 | mock.requestAnimationFrame.$resolve() 1284 | m.route("/test14?test&test2=") 1285 | mock.requestAnimationFrame.$resolve() //teardown 1286 | return mock.location.search == "?/test14?test&test2=" && m.route.param("test") === null && m.route.param("test2") === "" 1287 | }) 1288 | test(function() { 1289 | mock.requestAnimationFrame.$resolve() //setup 1290 | mock.location.search = "?" 1291 | 1292 | var root = mock.document.createElement("div") 1293 | m.route.mode = "search" 1294 | m.route(root, "/", { 1295 | "/": {controller: function() {}, view: function() {}}, 1296 | "/test12": {controller: function() {}, view: function() {}} 1297 | }) 1298 | mock.requestAnimationFrame.$resolve() 1299 | m.route("/test12", {a: "foo", b: "bar"}) 1300 | mock.requestAnimationFrame.$resolve() //teardown 1301 | return mock.location.search == "?/test12?a=foo&b=bar" && m.route.param("a") == "foo" && m.route.param("b") == "bar" 1302 | }) 1303 | test(function() { 1304 | mock.requestAnimationFrame.$resolve() //setup 1305 | mock.location.search = "?" 1306 | 1307 | var root = mock.document.createElement("div") 1308 | var route1, route2 1309 | m.route.mode = "search" 1310 | m.route(root, "/", { 1311 | "/": {controller: function() {route1 = m.route()}, view: function() {}}, 1312 | "/test13": {controller: function() {route2 = m.route()}, view: function() {}} 1313 | }) 1314 | mock.requestAnimationFrame.$resolve() 1315 | m.route("/test13") 1316 | mock.requestAnimationFrame.$resolve() //teardown 1317 | return route1 == "/" && route2 == "/test13" 1318 | }) 1319 | test(function() { 1320 | mock.requestAnimationFrame.$resolve() //setup 1321 | mock.location.search = "?" 1322 | 1323 | var root = mock.document.createElement("div") 1324 | var unloaded = 0 1325 | m.route.mode = "search" 1326 | m.route(root, "/", { 1327 | "/": { 1328 | controller: function() {}, 1329 | view: function() { 1330 | return m("div", { 1331 | config: function(el, init, ctx) { 1332 | ctx.onunload = function() { 1333 | unloaded++ 1334 | } 1335 | } 1336 | }) 1337 | } 1338 | }, 1339 | "/test14": {controller: function() {}, view: function() {}} 1340 | }) 1341 | mock.requestAnimationFrame.$resolve() 1342 | m.route("/test14") 1343 | mock.requestAnimationFrame.$resolve() //teardown 1344 | return unloaded == 1 1345 | }) 1346 | test(function() { 1347 | mock.requestAnimationFrame.$resolve() //setup 1348 | mock.location.search = "?" 1349 | 1350 | var root = mock.document.createElement("div") 1351 | var unloaded = 0 1352 | m.route.mode = "search" 1353 | m.route(root, "/", { 1354 | "/": { 1355 | controller: function() {}, 1356 | view: function() { 1357 | return [ 1358 | m("div"), 1359 | m("div", { 1360 | config: function(el, init, ctx) { 1361 | ctx.onunload = function() { 1362 | unloaded++ 1363 | } 1364 | } 1365 | }) 1366 | ] 1367 | } 1368 | }, 1369 | "/test15": { 1370 | controller: function() {}, 1371 | view: function() { 1372 | return [m("div")] 1373 | } 1374 | } 1375 | }) 1376 | mock.requestAnimationFrame.$resolve() 1377 | m.route("/test15") 1378 | mock.requestAnimationFrame.$resolve() //teardown 1379 | return unloaded == 1 1380 | }) 1381 | test(function() { 1382 | mock.requestAnimationFrame.$resolve() //setup 1383 | mock.location.search = "?" 1384 | 1385 | var root = mock.document.createElement("div") 1386 | var unloaded = 0 1387 | m.route.mode = "search" 1388 | m.route(root, "/", { 1389 | "/": { 1390 | controller: function() {}, 1391 | view: function() { 1392 | return m("div", { 1393 | config: function(el, init, ctx) { 1394 | ctx.onunload = function() { 1395 | unloaded++ 1396 | } 1397 | } 1398 | }) 1399 | } 1400 | }, 1401 | "/test16": { 1402 | controller: function() {}, 1403 | view: function() { 1404 | return m("a") 1405 | } 1406 | } 1407 | }) 1408 | mock.requestAnimationFrame.$resolve() 1409 | m.route("/test16") 1410 | mock.requestAnimationFrame.$resolve() //teardown 1411 | return unloaded == 1 1412 | }) 1413 | test(function() { 1414 | mock.requestAnimationFrame.$resolve() //setup 1415 | mock.location.search = "?" 1416 | 1417 | var root = mock.document.createElement("div") 1418 | var unloaded = 0 1419 | m.route.mode = "search" 1420 | m.route(root, "/", { 1421 | "/": { 1422 | controller: function() {}, 1423 | view: function() { 1424 | return [ 1425 | m("div", { 1426 | config: function(el, init, ctx) { 1427 | ctx.onunload = function() { 1428 | unloaded++ 1429 | } 1430 | } 1431 | }) 1432 | ] 1433 | } 1434 | }, 1435 | "/test17": { 1436 | controller: function() {}, 1437 | view: function() { 1438 | return m("a") 1439 | } 1440 | } 1441 | }) 1442 | mock.requestAnimationFrame.$resolve() 1443 | m.route("/test17") 1444 | mock.requestAnimationFrame.$resolve() //teardown 1445 | return unloaded == 1 1446 | }) 1447 | test(function() { 1448 | mock.requestAnimationFrame.$resolve() //setup 1449 | mock.location.search = "?" 1450 | 1451 | var root = mock.document.createElement("div") 1452 | var unloaded = 0 1453 | m.route.mode = "search" 1454 | m.route(root, "/", { 1455 | "/": { 1456 | controller: function() {}, 1457 | view: function() { 1458 | return m("div", { 1459 | config: function(el, init, ctx) { 1460 | ctx.onunload = function() { 1461 | unloaded++ 1462 | } 1463 | } 1464 | }) 1465 | } 1466 | }, 1467 | "/test18": { 1468 | controller: function() {}, 1469 | view: function() { 1470 | return [m("a")] 1471 | } 1472 | } 1473 | }) 1474 | mock.requestAnimationFrame.$resolve() 1475 | m.route("/test18") 1476 | mock.requestAnimationFrame.$resolve() //teardown 1477 | return unloaded == 1 1478 | }) 1479 | test(function() { 1480 | mock.requestAnimationFrame.$resolve() //setup 1481 | mock.location.search = "?" 1482 | 1483 | var root = mock.document.createElement("div") 1484 | var unloaded = 0 1485 | m.route.mode = "search" 1486 | m.route(root, "/", { 1487 | "/": { 1488 | controller: function() {}, 1489 | view: function() { 1490 | return [ 1491 | m("div", { 1492 | key: 1, 1493 | config: function(el, init, ctx) { 1494 | ctx.onunload = function() { 1495 | unloaded++ 1496 | } 1497 | } 1498 | }) 1499 | ] 1500 | } 1501 | }, 1502 | "/test20": { 1503 | controller: function() {}, 1504 | view: function() { 1505 | return [ 1506 | m("div", { 1507 | key: 2, 1508 | config: function(el, init, ctx) { 1509 | ctx.onunload = function() { 1510 | unloaded++ 1511 | } 1512 | } 1513 | }) 1514 | ] 1515 | } 1516 | } 1517 | }) 1518 | mock.requestAnimationFrame.$resolve() 1519 | m.route("/test20") 1520 | mock.requestAnimationFrame.$resolve() //teardown 1521 | return unloaded == 1 1522 | }) 1523 | test(function() { 1524 | mock.requestAnimationFrame.$resolve() //setup 1525 | mock.location.search = "?" 1526 | 1527 | var root = mock.document.createElement("div") 1528 | var unloaded = 0 1529 | m.route.mode = "search" 1530 | m.route(root, "/", { 1531 | "/": { 1532 | controller: function() {}, 1533 | view: function() { 1534 | return [ 1535 | m("div", { 1536 | key: 1, 1537 | config: function(el, init, ctx) { 1538 | ctx.onunload = function() { 1539 | unloaded++ 1540 | } 1541 | } 1542 | }) 1543 | ] 1544 | } 1545 | }, 1546 | "/test21": { 1547 | controller: function() {}, 1548 | view: function() { 1549 | return [ 1550 | m("div", { 1551 | config: function(el, init, ctx) { 1552 | ctx.onunload = function() { 1553 | unloaded++ 1554 | } 1555 | } 1556 | }) 1557 | ] 1558 | } 1559 | } 1560 | }) 1561 | mock.requestAnimationFrame.$resolve() 1562 | m.route("/test21") 1563 | mock.requestAnimationFrame.$resolve() //teardown 1564 | return unloaded == 1 1565 | }) 1566 | test(function() { 1567 | mock.requestAnimationFrame.$resolve() //setup 1568 | mock.location.search = "?" 1569 | 1570 | var root = mock.document.createElement("div") 1571 | m.route.mode = "search" 1572 | m.route(root, "/foo", { 1573 | "/foo": { 1574 | controller: function() {}, 1575 | view: function() { 1576 | return m("div", "foo"); 1577 | } 1578 | }, 1579 | "/bar": { 1580 | controller: function() {}, 1581 | view: function() { 1582 | return m("div", "bar"); 1583 | } 1584 | }, 1585 | }) 1586 | mock.requestAnimationFrame.$resolve() 1587 | var foo = root.childNodes[0].childNodes[0].nodeValue; 1588 | m.route("/bar") 1589 | mock.requestAnimationFrame.$resolve() //teardown 1590 | var bar = root.childNodes[0].childNodes[0].nodeValue; 1591 | return (foo === "foo" && bar === "bar") 1592 | }) 1593 | test(function() { 1594 | mock.requestAnimationFrame.$resolve() //setup 1595 | mock.location.search = "?" 1596 | 1597 | var root = mock.document.createElement("div") 1598 | var unloaded = 0 1599 | var config = function(el, init, ctx) { 1600 | ctx.onunload = function() { 1601 | unloaded++ 1602 | } 1603 | } 1604 | m.route.mode = "search" 1605 | m.route(root, "/foo1", { 1606 | "/foo1": { 1607 | controller: function() {}, 1608 | view: function() { 1609 | return m("div", m("a", {config: config}, "foo")); 1610 | } 1611 | }, 1612 | "/bar1": { 1613 | controller: function() {}, 1614 | view: function() { 1615 | return m("main", m("a", {config: config}, "foo")); 1616 | } 1617 | }, 1618 | }) 1619 | mock.requestAnimationFrame.$resolve() 1620 | m.route("/bar1") 1621 | mock.requestAnimationFrame.$resolve() //teardown 1622 | return unloaded == 1 1623 | }) 1624 | test(function() { 1625 | mock.requestAnimationFrame.$resolve() //setup 1626 | mock.location.search = "?" 1627 | 1628 | var root = mock.document.createElement("div") 1629 | var strategy 1630 | m.route.mode = "search" 1631 | m.route(root, "/foo1", { 1632 | "/foo1": { 1633 | controller: function() { 1634 | strategy = m.redraw.strategy() 1635 | m.redraw.strategy("none") 1636 | }, 1637 | view: function() { 1638 | return m("div"); 1639 | } 1640 | } 1641 | }) 1642 | mock.requestAnimationFrame.$resolve() //teardown 1643 | return strategy == "all" && root.childNodes.length == 0 1644 | }) 1645 | test(function() { 1646 | mock.requestAnimationFrame.$resolve() //setup 1647 | mock.location.search = "?" 1648 | 1649 | var root = mock.document.createElement("div") 1650 | var strategy, count = 0 1651 | var config = function(el, init) {if (!init) count++} 1652 | m.route.mode = "search" 1653 | m.route(root, "/foo1", { 1654 | "/foo1": { 1655 | controller: function() {}, 1656 | view: function() { 1657 | return m("div", {config: config}); 1658 | } 1659 | }, 1660 | "/bar1": { 1661 | controller: function() { 1662 | strategy = m.redraw.strategy() 1663 | m.redraw.strategy("redraw") 1664 | }, 1665 | view: function() { 1666 | return m("div", {config: config}); 1667 | } 1668 | }, 1669 | }) 1670 | mock.requestAnimationFrame.$resolve() 1671 | m.route("/bar1") 1672 | mock.requestAnimationFrame.$resolve() //teardown 1673 | return strategy == "all" && count == 1 1674 | }) 1675 | test(function() { 1676 | mock.requestAnimationFrame.$resolve() //setup 1677 | mock.location.search = "?" 1678 | 1679 | var root = mock.document.createElement("div") 1680 | var strategy 1681 | m.route.mode = "search" 1682 | m.route(root, "/foo1", { 1683 | "/foo1": { 1684 | controller: function() {this.number = 1}, 1685 | view: function(ctrl) { 1686 | return m("div", {onclick: function() { 1687 | strategy = m.redraw.strategy() 1688 | ctrl.number++ 1689 | m.redraw.strategy("none") 1690 | }}, ctrl.number); 1691 | } 1692 | } 1693 | }) 1694 | root.childNodes[0].onclick({}) 1695 | mock.requestAnimationFrame.$resolve() //teardown 1696 | return strategy == "diff" && root.childNodes[0].childNodes[0].nodeValue == "1" 1697 | }) 1698 | test(function() { 1699 | mock.requestAnimationFrame.$resolve() //setup 1700 | mock.location.search = "?" 1701 | 1702 | var root = mock.document.createElement("div") 1703 | var count = 0 1704 | var config = function(el, init ) {if (!init) count++} 1705 | m.route.mode = "search" 1706 | m.route(root, "/foo1", { 1707 | "/foo1": { 1708 | controller: function() {}, 1709 | view: function(ctrl) { 1710 | return m("div", {config: config, onclick: function() { 1711 | m.redraw.strategy("all") 1712 | }}); 1713 | } 1714 | } 1715 | }) 1716 | root.childNodes[0].onclick({}) 1717 | mock.requestAnimationFrame.$resolve() //teardown 1718 | return count == 2 1719 | }) 1720 | test(function() { 1721 | mock.requestAnimationFrame.$resolve() //setup 1722 | mock.location.search = "?" 1723 | 1724 | var root = mock.document.createElement("div") 1725 | var value 1726 | m.route(root, "/foo+bar", { 1727 | "/:arg": { 1728 | controller: function() {value = m.route.param("arg")}, 1729 | view: function(ctrl) { 1730 | return "" 1731 | } 1732 | } 1733 | }) 1734 | return value == "foo+bar" 1735 | }) 1736 | test(function() { 1737 | mock.requestAnimationFrame.$resolve() //setup 1738 | mock.location.search = "?" 1739 | 1740 | var root = mock.document.createElement("div") 1741 | m.route.mode = "search" 1742 | m.route(root, "/", { 1743 | "/": {controller: function() {}, view: function() {return "foo"}}, 1744 | "/test22": {controller: function() {}, view: function() {return "bar"}} 1745 | }) 1746 | mock.requestAnimationFrame.$resolve() 1747 | m.route(String("/test22/")) 1748 | mock.requestAnimationFrame.$resolve() //teardown 1749 | return mock.location.search == "?/test22/" && root.childNodes[0].nodeValue === "bar" 1750 | }) 1751 | test(function() { 1752 | mock.requestAnimationFrame.$resolve() //setup 1753 | mock.location.search = "?" 1754 | 1755 | var root = mock.document.createElement("div") 1756 | m.route.mode = "search" 1757 | m.route(root, "/", { 1758 | "/": {controller: function() {}, view: function() {return "foo"}}, 1759 | "/test23": {controller: function() {}, view: function() {return "bar"}} 1760 | }) 1761 | mock.requestAnimationFrame.$resolve() 1762 | m.route(new String("/test23/")) 1763 | mock.requestAnimationFrame.$resolve() //teardown 1764 | return mock.location.search == "?/test23/" && root.childNodes[0].nodeValue === "bar" 1765 | }) 1766 | test(function() { 1767 | mock.requestAnimationFrame.$resolve() //setup 1768 | mock.location.search = "?" 1769 | 1770 | var root = mock.document.createElement("div") 1771 | var value 1772 | m.route(root, String("/foo+bar"), { 1773 | "/:arg": { 1774 | controller: function() {value = m.route.param("arg")}, 1775 | view: function(ctrl) { 1776 | return "" 1777 | } 1778 | } 1779 | }) 1780 | return value == "foo+bar" 1781 | }) 1782 | test(function() { 1783 | mock.requestAnimationFrame.$resolve() //setup 1784 | mock.location.search = "?" 1785 | 1786 | var root = mock.document.createElement("div") 1787 | var value 1788 | m.route(root, new String("/foo+bar"), { 1789 | "/:arg": { 1790 | controller: function() {value = m.route.param("arg")}, 1791 | view: function(ctrl) { 1792 | return "" 1793 | } 1794 | } 1795 | }) 1796 | return value == "foo+bar" 1797 | }) 1798 | test(function() { 1799 | mock.requestAnimationFrame.$resolve() 1800 | mock.location.search = "?" 1801 | 1802 | var root = mock.document.createElement("div") 1803 | 1804 | var a = {} 1805 | a.controller = function() {m.route("/b")} 1806 | a.view = function() {return "a"} 1807 | 1808 | var b = {} 1809 | b.controller = function() {} 1810 | b.view = function(ctrl) {return "b"} 1811 | 1812 | m.route(root, "/a", { 1813 | "/a": a, 1814 | "/b": b 1815 | }) 1816 | mock.requestAnimationFrame.$resolve() 1817 | 1818 | return root.childNodes[0].nodeValue == "b" 1819 | }) 1820 | test(function() { 1821 | mock.requestAnimationFrame.$resolve() 1822 | mock.location.search = "?" 1823 | 1824 | var root = mock.document.createElement("div") 1825 | 1826 | var a = {} 1827 | a.controller = function() { 1828 | m.route("/b?foo=1", {foo: 2}) 1829 | } 1830 | a.view = function() {return "a"} 1831 | 1832 | var b = {} 1833 | b.controller = function() {} 1834 | b.view = function() {return "b"} 1835 | 1836 | m.route(root, "/", { 1837 | "/": a, 1838 | "/b": b, 1839 | }) 1840 | mock.requestAnimationFrame.$resolve() 1841 | 1842 | return mock.location.search == "?/b?foo=2" 1843 | }) 1844 | test(function() { 1845 | mock.requestAnimationFrame.$resolve() 1846 | mock.location.search = "?" 1847 | mock.history.$$length = 0 1848 | 1849 | var root = mock.document.createElement("div") 1850 | 1851 | var a = {} 1852 | a.controller = function() {} 1853 | a.view = function() {return "a"} 1854 | 1855 | var b = {} 1856 | b.controller = function() {} 1857 | b.view = function() {return "b"} 1858 | 1859 | m.route(root, "/a", { 1860 | "/a": a, 1861 | "/b": b, 1862 | }) 1863 | mock.requestAnimationFrame.$resolve() 1864 | 1865 | m.route("/b") 1866 | 1867 | mock.requestAnimationFrame.$resolve() 1868 | 1869 | return mock.history.$$length == 1 1870 | }) 1871 | test(function() { 1872 | mock.requestAnimationFrame.$resolve() 1873 | mock.location.search = "?" 1874 | mock.history.$$length = 0 1875 | 1876 | var root = mock.document.createElement("div") 1877 | 1878 | var a = {} 1879 | a.controller = function() {} 1880 | a.view = function() {return "a"} 1881 | 1882 | var b = {} 1883 | b.controller = function() {} 1884 | b.view = function() {return "b"} 1885 | 1886 | m.route(root, "/a", { 1887 | "/a": a, 1888 | "/b": b, 1889 | }) 1890 | mock.requestAnimationFrame.$resolve() 1891 | 1892 | m.route("/a") 1893 | 1894 | mock.requestAnimationFrame.$resolve() 1895 | 1896 | return mock.history.$$length == 0 1897 | }) 1898 | test(function() { 1899 | mock.requestAnimationFrame.$resolve() 1900 | mock.location.search = "?" 1901 | 1902 | var root = mock.document.createElement("div") 1903 | var initCount = 0 1904 | 1905 | var a = {} 1906 | a.controller = function() {} 1907 | a.view = function() { 1908 | return m("a", {config: function(el, init, ctx) { 1909 | if (!init) initCount++ 1910 | }}) 1911 | } 1912 | 1913 | var b = {} 1914 | b.controller = function() {} 1915 | b.view = a.view 1916 | 1917 | m.route(root, "/a", { 1918 | "/a": a, 1919 | "/b": b, 1920 | }) 1921 | mock.requestAnimationFrame.$resolve() 1922 | 1923 | m.route("/b") 1924 | 1925 | mock.requestAnimationFrame.$resolve() 1926 | 1927 | return initCount == 2 1928 | }) 1929 | test(function() { 1930 | mock.requestAnimationFrame.$resolve() 1931 | mock.location.search = "?" 1932 | 1933 | var root = mock.document.createElement("div") 1934 | var value 1935 | 1936 | var a = {} 1937 | a.controller = function() {} 1938 | a.view = function() { 1939 | return m("a", {config: function(el, init, ctx) { 1940 | value = ctx.retain 1941 | }}) 1942 | } 1943 | 1944 | m.route(root, "/a", { 1945 | "/a": a 1946 | }) 1947 | 1948 | return !value 1949 | }) 1950 | test(function() { 1951 | mock.requestAnimationFrame.$resolve() 1952 | mock.location.search = "?" 1953 | 1954 | var root = mock.document.createElement("div") 1955 | var value 1956 | 1957 | var a = {} 1958 | a.controller = function() {m.redraw.strategy("diff")} 1959 | a.view = function() { 1960 | return m("a", {config: function(el, init, ctx) { 1961 | value = ctx.retain 1962 | }}) 1963 | } 1964 | 1965 | m.route(root, "/a", { 1966 | "/a": a 1967 | }) 1968 | 1969 | return value 1970 | }) 1971 | test(function() { 1972 | mock.requestAnimationFrame.$resolve() 1973 | mock.location.search = "?" 1974 | 1975 | var root = mock.document.createElement("div") 1976 | var initCount = 0 1977 | 1978 | var a = {} 1979 | a.controller = function() {} 1980 | a.view = function() { 1981 | return m("a", {config: function(el, init, ctx) { 1982 | ctx.retain = false 1983 | if (!init) initCount++ 1984 | }}) 1985 | } 1986 | 1987 | var b = {} 1988 | b.controller = function() {} 1989 | b.view = a.view 1990 | 1991 | m.route(root, "/a", { 1992 | "/a": a, 1993 | "/b": b, 1994 | }) 1995 | mock.requestAnimationFrame.$resolve() 1996 | 1997 | m.route("/b") 1998 | 1999 | mock.requestAnimationFrame.$resolve() 2000 | 2001 | return initCount == 2 2002 | }) 2003 | test(function() { 2004 | mock.requestAnimationFrame.$resolve() 2005 | mock.location.search = "?" 2006 | 2007 | var root = mock.document.createElement("div") 2008 | var initCount = 0 2009 | 2010 | var a = {} 2011 | a.controller = function() {} 2012 | a.view = function() { 2013 | return m("a", {config: function(el, init, ctx) { 2014 | ctx.retain = true 2015 | if (!init) initCount++ 2016 | }}) 2017 | } 2018 | 2019 | var b = {} 2020 | b.controller = function() {} 2021 | b.view = a.view 2022 | 2023 | m.route(root, "/a", { 2024 | "/a": a, 2025 | "/b": b, 2026 | }) 2027 | mock.requestAnimationFrame.$resolve() 2028 | 2029 | m.route("/b") 2030 | 2031 | mock.requestAnimationFrame.$resolve() 2032 | 2033 | return initCount == 1 2034 | }) 2035 | test(function() { 2036 | mock.requestAnimationFrame.$resolve() 2037 | mock.location.search = "?" 2038 | 2039 | var root = mock.document.createElement("div") 2040 | var initCount = 0 2041 | 2042 | var a = {} 2043 | a.controller = function() {m.redraw.strategy("diff")} 2044 | a.view = function() { 2045 | return m("a", {config: function(el, init, ctx) { 2046 | if (!init) initCount++ 2047 | }}) 2048 | } 2049 | 2050 | var b = {} 2051 | b.controller = function() {m.redraw.strategy("diff")} 2052 | b.view = a.view 2053 | 2054 | m.route(root, "/a", { 2055 | "/a": a, 2056 | "/b": b, 2057 | }) 2058 | mock.requestAnimationFrame.$resolve() 2059 | 2060 | m.route("/b") 2061 | 2062 | mock.requestAnimationFrame.$resolve() 2063 | 2064 | return initCount == 1 2065 | }) 2066 | test(function() { 2067 | mock.requestAnimationFrame.$resolve() 2068 | mock.location.search = "?" 2069 | 2070 | var root = mock.document.createElement("div") 2071 | var initCount = 0 2072 | 2073 | var a = {} 2074 | a.controller = function() {m.redraw.strategy("diff")} 2075 | a.view = function() { 2076 | return m("a", {config: function(el, init, ctx) { 2077 | ctx.retain = true 2078 | if (!init) initCount++ 2079 | }}) 2080 | } 2081 | 2082 | var b = {} 2083 | b.controller = function() {m.redraw.strategy("diff")} 2084 | b.view = a.view 2085 | 2086 | m.route(root, "/a", { 2087 | "/a": a, 2088 | "/b": b, 2089 | }) 2090 | mock.requestAnimationFrame.$resolve() 2091 | 2092 | m.route("/b") 2093 | 2094 | mock.requestAnimationFrame.$resolve() 2095 | 2096 | return initCount == 1 2097 | }) 2098 | test(function() { 2099 | mock.requestAnimationFrame.$resolve() 2100 | mock.location.search = "?" 2101 | 2102 | var root = mock.document.createElement("div") 2103 | var initCount = 0 2104 | 2105 | var a = {} 2106 | a.controller = function() {m.redraw.strategy("diff")} 2107 | a.view = function() { 2108 | return m("a", {config: function(el, init, ctx) { 2109 | ctx.retain = false 2110 | if (!init) initCount++ 2111 | }}) 2112 | } 2113 | 2114 | var b = {} 2115 | b.controller = function() {m.redraw.strategy("diff")} 2116 | b.view = a.view 2117 | 2118 | m.route(root, "/a", { 2119 | "/a": a, 2120 | "/b": b, 2121 | }) 2122 | mock.requestAnimationFrame.$resolve() 2123 | 2124 | m.route("/b") 2125 | 2126 | mock.requestAnimationFrame.$resolve() 2127 | 2128 | return initCount == 2 2129 | }) 2130 | test(function() { 2131 | mock.requestAnimationFrame.$resolve() 2132 | mock.location.search = "?" 2133 | 2134 | var root = mock.document.createElement("div") 2135 | var initCount = 0 2136 | 2137 | var a = {} 2138 | a.controller = function() {} 2139 | a.view = function() { 2140 | return m("div", m("a", {config: function(el, init, ctx) { 2141 | ctx.retain = true 2142 | if (!init) initCount++ 2143 | }})) 2144 | } 2145 | 2146 | var b = {} 2147 | b.controller = function() {} 2148 | b.view = function() { 2149 | return m("section", m("a", {config: function(el, init, ctx) { 2150 | ctx.retain = true 2151 | if (!init) initCount++ 2152 | }})) 2153 | } 2154 | 2155 | m.route(root, "/a", { 2156 | "/a": a, 2157 | "/b": b, 2158 | }) 2159 | mock.requestAnimationFrame.$resolve() 2160 | 2161 | m.route("/b") 2162 | 2163 | mock.requestAnimationFrame.$resolve() 2164 | 2165 | return initCount == 1 2166 | }) 2167 | test(function() { 2168 | mock.requestAnimationFrame.$resolve() 2169 | mock.location.search = "?" 2170 | 2171 | var root = mock.document.createElement("div") 2172 | var initCount = 0 2173 | 2174 | var a = {} 2175 | a.controller = function() {} 2176 | a.view = function() { 2177 | return m("div", m("a", {config: function(el, init, ctx) { 2178 | ctx.retain = false 2179 | if (!init) initCount++ 2180 | }})) 2181 | } 2182 | 2183 | var b = {} 2184 | b.controller = function() {} 2185 | b.view = function() { 2186 | return m("section", m("a", {config: function(el, init, ctx) { 2187 | ctx.retain = false 2188 | if (!init) initCount++ 2189 | }})) 2190 | } 2191 | 2192 | m.route(root, "/a", { 2193 | "/a": a, 2194 | "/b": b, 2195 | }) 2196 | mock.requestAnimationFrame.$resolve() 2197 | 2198 | m.route("/b") 2199 | 2200 | mock.requestAnimationFrame.$resolve() 2201 | 2202 | return initCount == 2 2203 | }) 2204 | test(function() { 2205 | mock.requestAnimationFrame.$resolve() 2206 | mock.location.search = "?" 2207 | 2208 | var root = mock.document.createElement("div") 2209 | var initCount = 0 2210 | 2211 | var a = {} 2212 | a.controller = function() {} 2213 | a.view = function() { 2214 | return m("div", m("a", {config: function(el, init, ctx) { 2215 | if (!init) initCount++ 2216 | }})) 2217 | } 2218 | 2219 | var b = {} 2220 | b.controller = function() {} 2221 | b.view = function() { 2222 | return m("section", m("a", {config: function(el, init, ctx) { 2223 | if (!init) initCount++ 2224 | }})) 2225 | } 2226 | 2227 | m.route(root, "/a", { 2228 | "/a": a, 2229 | "/b": b, 2230 | }) 2231 | mock.requestAnimationFrame.$resolve() 2232 | 2233 | m.route("/b") 2234 | 2235 | mock.requestAnimationFrame.$resolve() 2236 | 2237 | return initCount == 2 2238 | }) 2239 | test(function() { 2240 | mock.requestAnimationFrame.$resolve() 2241 | mock.location.search = "?" 2242 | 2243 | var root = mock.document.createElement("div") 2244 | var initCount = 0 2245 | 2246 | var a = {} 2247 | a.controller = function() {m.redraw.strategy("diff")} 2248 | a.view = function() { 2249 | return m("div", m("a", {config: function(el, init, ctx) { 2250 | ctx.retain = true 2251 | if (!init) initCount++ 2252 | }})) 2253 | } 2254 | 2255 | var b = {} 2256 | b.controller = function() {m.redraw.strategy("diff")} 2257 | b.view = function() { 2258 | return m("section", m("a", {config: function(el, init, ctx) { 2259 | ctx.retain = true 2260 | if (!init) initCount++ 2261 | }})) 2262 | } 2263 | 2264 | m.route(root, "/a", { 2265 | "/a": a, 2266 | "/b": b, 2267 | }) 2268 | mock.requestAnimationFrame.$resolve() 2269 | 2270 | m.route("/b") 2271 | 2272 | mock.requestAnimationFrame.$resolve() 2273 | 2274 | return initCount == 1 2275 | }) 2276 | test(function() { 2277 | mock.requestAnimationFrame.$resolve() 2278 | mock.location.search = "?" 2279 | 2280 | var root = mock.document.createElement("div") 2281 | var initCount = 0 2282 | 2283 | var a = {} 2284 | a.controller = function() {m.redraw.strategy("diff")} 2285 | a.view = function() { 2286 | return m("div", m("a", {config: function(el, init, ctx) { 2287 | ctx.retain = false 2288 | if (!init) initCount++ 2289 | }})) 2290 | } 2291 | 2292 | var b = {} 2293 | b.controller = function() {m.redraw.strategy("diff")} 2294 | b.view = function() { 2295 | return m("section", m("a", {config: function(el, init, ctx) { 2296 | ctx.retain = false 2297 | if (!init) initCount++ 2298 | }})) 2299 | } 2300 | 2301 | m.route(root, "/a", { 2302 | "/a": a, 2303 | "/b": b, 2304 | }) 2305 | mock.requestAnimationFrame.$resolve() 2306 | 2307 | m.route("/b") 2308 | 2309 | mock.requestAnimationFrame.$resolve() 2310 | 2311 | return initCount == 2 2312 | }) 2313 | test(function() { 2314 | mock.requestAnimationFrame.$resolve() 2315 | mock.location.search = "?" 2316 | 2317 | var root = mock.document.createElement("div") 2318 | var initCount = 0 2319 | 2320 | var a = {} 2321 | a.controller = function() {m.redraw.strategy("diff")} 2322 | a.view = function() { 2323 | return m("div", m("a", {config: function(el, init, ctx) { 2324 | if (!init) initCount++ 2325 | }})) 2326 | } 2327 | 2328 | var b = {} 2329 | b.controller = function() {m.redraw.strategy("diff")} 2330 | b.view = function() { 2331 | return m("section", m("a", {config: function(el, init, ctx) { 2332 | if (!init) initCount++ 2333 | }})) 2334 | } 2335 | 2336 | m.route(root, "/a", { 2337 | "/a": a, 2338 | "/b": b, 2339 | }) 2340 | mock.requestAnimationFrame.$resolve() 2341 | 2342 | m.route("/b") 2343 | 2344 | mock.requestAnimationFrame.$resolve() 2345 | 2346 | return initCount == 1 2347 | }) 2348 | //end m.route 2349 | 2350 | //m.route.parseQueryString 2351 | test(function() { 2352 | var args = m.route.parseQueryString("foo=bar&hello%5B%5D=world&hello%5B%5D=mars&hello%5B%5D=pluto") 2353 | return args["hello[]"] instanceof Array && args["hello[]"].indexOf("world") > -1 && args["hello[]"].indexOf("mars") > -1 && args["hello[]"].indexOf("pluto") > -1 2354 | }) 2355 | test(function() { 2356 | var args = m.route.parseQueryString("foo=bar&hello=world&hello=mars&bam=&yup") 2357 | return args.foo === "bar" && args.hello[0] === "world" && args.hello[1] === "mars" && args.bam === "" && args.yup === null 2358 | }) 2359 | 2360 | //m.route.buildQueryString 2361 | test(function() { 2362 | var string = m.route.buildQueryString({ 2363 | foo: "bar", 2364 | hello: ["world", "mars", "mars"], 2365 | world: { 2366 | test:3 2367 | }, 2368 | bam: "", 2369 | yup: null, 2370 | removed: undefined 2371 | }) 2372 | return string === "foo=bar&hello=world&hello=mars&world%5Btest%5D=3&bam=&yup" 2373 | }) 2374 | 2375 | //m.prop 2376 | test(function() { 2377 | var prop = m.prop("test") 2378 | return prop() === "test" 2379 | }) 2380 | test(function() { 2381 | var prop = m.prop("test") 2382 | prop("foo") 2383 | return prop() === "foo" 2384 | }) 2385 | test(function() { 2386 | var prop = m.prop("test") 2387 | return JSON.stringify(prop) === '"test"' 2388 | }) 2389 | test(function() { 2390 | var obj = {prop: m.prop("test")} 2391 | return JSON.stringify(obj) === '{"prop":"test"}' 2392 | }) 2393 | test(function() { 2394 | var defer = m.deferred() 2395 | var prop = m.prop(defer.promise) 2396 | defer.resolve("test") 2397 | 2398 | return prop() === "test" 2399 | }) 2400 | test(function() { 2401 | var defer = m.deferred() 2402 | var prop = m.prop(defer.promise).then(function () { 2403 | return "test2" 2404 | }) 2405 | defer.resolve("test") 2406 | 2407 | return prop() === "test2" 2408 | }) 2409 | test(function() { 2410 | var prop = m.prop(null) 2411 | return prop() === null 2412 | }) 2413 | 2414 | //m.request 2415 | test(function() { 2416 | var prop = m.request({method: "GET", url: "test"}) 2417 | mock.XMLHttpRequest.$instances.pop().onreadystatechange() 2418 | return prop().method === "GET" && prop().url === "test" 2419 | }) 2420 | test(function() { 2421 | var prop = m.request({method: "GET", url: "test"}).then(function(value) {return "foo"}) 2422 | mock.XMLHttpRequest.$instances.pop().onreadystatechange() 2423 | return prop() === "foo" 2424 | }) 2425 | test(function() { 2426 | var prop = m.request({method: "POST", url: "http://domain.com:80", data: {}}).then(function(value) {return value}) 2427 | mock.XMLHttpRequest.$instances.pop().onreadystatechange() 2428 | return prop().url === "http://domain.com:80" 2429 | }) 2430 | test(function() { 2431 | var prop = m.request({method: "POST", url: "http://domain.com:80/:test1", data: {test1: "foo"}}).then(function(value) {return value}) 2432 | mock.XMLHttpRequest.$instances.pop().onreadystatechange() 2433 | return prop().url === "http://domain.com:80/foo" 2434 | }) 2435 | test(function() { 2436 | var error = m.prop("no error") 2437 | var prop = m.request({method: "GET", url: "test", deserialize: function() {throw new Error("error occurred")}}).then(null, error) 2438 | mock.XMLHttpRequest.$instances.pop().onreadystatechange() 2439 | return prop().message === "error occurred" && error().message === "error occurred" 2440 | }) 2441 | test(function() { 2442 | var error = m.prop("no error"), exception 2443 | var prop = m.request({method: "GET", url: "test", deserialize: function() {throw new TypeError("error occurred")}}).then(null, error) 2444 | try {mock.XMLHttpRequest.$instances.pop().onreadystatechange()} 2445 | catch (e) {exception = e} 2446 | m.endComputation() 2447 | return prop() === undefined && error() === "no error" && exception.message == "error occurred" 2448 | }) 2449 | test(function() { 2450 | var error = m.prop("no error") 2451 | var prop = m.request({method: "POST", url: "test", data: {foo: 1}}).then(null, error) 2452 | var xhr = mock.XMLHttpRequest.$instances.pop() 2453 | xhr.onreadystatechange() 2454 | return xhr.$headers["Content-Type"] == "application/json; charset=utf-8" 2455 | }) 2456 | test(function() { 2457 | var error = m.prop("no error") 2458 | var prop = m.request({method: "POST", url: "test"}).then(null, error) 2459 | var xhr = mock.XMLHttpRequest.$instances.pop() 2460 | xhr.onreadystatechange() 2461 | return xhr.$headers["Content-Type"] === undefined 2462 | }) 2463 | test(function() { 2464 | var prop = m.request({method: "POST", url: "test", initialValue: "foo"}).then(function(data) { return data; }) 2465 | var initialValue = prop(); 2466 | mock.XMLHttpRequest.$instances.pop().onreadystatechange() 2467 | 2468 | return initialValue === "foo" 2469 | }) 2470 | test(function() { 2471 | var prop = m.request({method: "POST", url: "test", initialValue: "foo"}) 2472 | var initialValue = prop(); 2473 | mock.XMLHttpRequest.$instances.pop().onreadystatechange() 2474 | 2475 | return initialValue === "foo" 2476 | }) 2477 | test(function() { 2478 | var prop = m.request({method: "POST", url: "test", initialValue: "foo"}).then(function(value) {return "bar"}) 2479 | mock.XMLHttpRequest.$instances.pop().onreadystatechange() 2480 | return prop() === "bar" 2481 | }) 2482 | test(function() { 2483 | var prop = m.request({method: "GET", url: "test", data: {foo: 1}}) 2484 | mock.XMLHttpRequest.$instances.pop().onreadystatechange() 2485 | return prop().url === "test?foo=1" 2486 | }) 2487 | test(function() { 2488 | var prop = m.request({method: "POST", url: "test", data: {foo: 1}}) 2489 | mock.XMLHttpRequest.$instances.pop().onreadystatechange() 2490 | return prop().url === "test" 2491 | }) 2492 | test(function() { 2493 | var prop = m.request({method: "GET", url: "test", data: {foo: [1, 2]}}) 2494 | mock.XMLHttpRequest.$instances.pop().onreadystatechange() 2495 | return prop().url === "test?foo=1&foo=2" 2496 | }) 2497 | test(function() { 2498 | var value 2499 | var prop1 = m.request({method: "GET", url: "test", initialValue: 123}) 2500 | var val1 = prop1() 2501 | var prop2 = prop1.then(function() {return 1}) 2502 | var val2 = prop2() 2503 | var prop3 = prop1.then(function(v) {value = v}) 2504 | var val3 = prop3() 2505 | mock.XMLHttpRequest.$instances.pop().onreadystatechange() 2506 | return val1 === 123 && val2 === 123 && val3 === 123 && value.method === "GET" && value.url === "test" 2507 | }) 2508 | 2509 | // m.request over jsonp 2510 | test(function(){ 2511 | // script tags cannot be appended directly on the document 2512 | var body = mock.document.createElement("body") 2513 | mock.document.body = body 2514 | mock.document.appendChild(body) 2515 | 2516 | var error = m.prop("no error") 2517 | var data 2518 | var req = m.request({url: "/test", dataType: "jsonp"}).then(function(received) {data = received}, error) 2519 | var callbackKey = Object.keys(mock).filter(function(globalKey){ 2520 | return globalKey.indexOf("mithril_callback") > -1 2521 | }).pop() 2522 | var scriptTag = [].slice.call(mock.document.getElementsByTagName("script")).filter(function(script){ 2523 | return script.src.indexOf(callbackKey) > -1 2524 | }).pop() 2525 | mock[callbackKey]({foo: "bar"}) 2526 | mock.document.removeChild(body) 2527 | return scriptTag.src.indexOf("/test?callback=mithril_callback") > -1 && data.foo == "bar" 2528 | }) 2529 | test(function(){ 2530 | // script tags cannot be appended directly on the document 2531 | var body = mock.document.createElement("body") 2532 | mock.document.body = body 2533 | mock.document.appendChild(body) 2534 | 2535 | var error = m.prop("no error") 2536 | var data 2537 | var req = m.request({url: "/test", dataType: "jsonp", callbackKey: "jsonpCallback"}).then(function(received) {data = received}, error); 2538 | var callbackKey = Object.keys(mock).filter(function(globalKey){ 2539 | return globalKey.indexOf("mithril_callback") > -1 2540 | }).pop() 2541 | var scriptTag = [].slice.call(mock.document.getElementsByTagName("script")).filter(function(script){ 2542 | return script.src.indexOf(callbackKey) > -1 2543 | }).pop() 2544 | mock[callbackKey]({foo: "bar1"}) 2545 | mock.document.removeChild(body) 2546 | return scriptTag.src.indexOf("/test?jsonpCallback=mithril_callback") > -1 && data.foo == "bar1" 2547 | }) 2548 | test(function(){ 2549 | var body = mock.document.createElement("body") 2550 | mock.document.body = body 2551 | mock.document.appendChild(body) 2552 | 2553 | var req = m.request({url: "/test", dataType: "jsonp"}) 2554 | var callbackKey = Object.keys(mock).filter(function(globalKey){ 2555 | return globalKey.indexOf("mithril_callback") > -1 2556 | }).pop() 2557 | var scriptTag = [].slice.call(mock.document.getElementsByTagName("script")).filter(function(script){ 2558 | return script.src.indexOf(callbackKey) > -1 2559 | }).pop(); 2560 | mock[callbackKey]({foo: "bar1"}) 2561 | var out = {foo: "bar1"} 2562 | mock.document.removeChild(body) 2563 | return JSON.stringify(out) === JSON.stringify(req()) 2564 | }) 2565 | test(function(){ 2566 | var body = mock.document.createElement("body") 2567 | mock.document.body = body 2568 | mock.document.appendChild(body) 2569 | 2570 | var req = m.request({url: "/test", dataType: "jsonp", data: {foo: "bar"}}) 2571 | var callbackKey = Object.keys(mock).filter(function(globalKey){ 2572 | return globalKey.indexOf("mithril_callback") > -1 2573 | }).pop() 2574 | var scriptTag = [].slice.call(mock.document.getElementsByTagName("script")).filter(function(script){ 2575 | return script.src.indexOf(callbackKey) > -1 2576 | }).pop(); 2577 | mock[callbackKey]({foo: "bar"}) 2578 | return scriptTag.src.indexOf("foo=bar") > -1 2579 | }) 2580 | test(function(){ 2581 | var body = mock.document.createElement("body"); 2582 | mock.document.body = body; 2583 | mock.document.appendChild(body); 2584 | 2585 | var _window = mock; 2586 | var error = m.prop(false); 2587 | var req = m.request({url: "/test", dataType: "jsonp", method: "GET", data: {foo: "bar"}}); 2588 | var callbackKey = Object.keys(mock).filter(function(globalKey){ 2589 | return globalKey.indexOf("mithril_callback") > -1 2590 | }).pop() 2591 | var scriptTag = [].slice.call(mock.document.getElementsByTagName("script")).filter(function(script){ 2592 | return script.src.indexOf(callbackKey) > -1 2593 | }).pop(); 2594 | mock[callbackKey]({foo: "bar"}) 2595 | mock.document.removeChild(body); 2596 | return scriptTag.src.match(/foo=bar/g).length == 1; 2597 | }) 2598 | 2599 | //m.deferred 2600 | test(function() { 2601 | var value 2602 | var deferred = m.deferred() 2603 | deferred.promise.then(function(data) {value = data}) 2604 | deferred.resolve("test") 2605 | return value === "test" 2606 | }) 2607 | test(function() { 2608 | var value 2609 | var deferred = m.deferred() 2610 | deferred.promise.then(function(data) {return "foo"}).then(function(data) {value = data}) 2611 | deferred.resolve("test") 2612 | return value === "foo" 2613 | }) 2614 | test(function() { 2615 | var value 2616 | var deferred = m.deferred() 2617 | deferred.promise.then(null, function(data) {value = data}) 2618 | deferred.reject("test") 2619 | return value === "test" 2620 | }) 2621 | test(function() { 2622 | var value 2623 | var deferred = m.deferred() 2624 | deferred.promise.then(null, function(data) {return "foo"}).then(function(data) {value = data}) 2625 | deferred.reject("test") 2626 | return value === "foo" 2627 | }) 2628 | test(function() { 2629 | var value1, value2 2630 | var deferred = m.deferred() 2631 | deferred.promise.then(function(data) {throw new Error}).then(function(data) {value1 = 1}, function(data) {value2 = data}) 2632 | deferred.resolve("test") 2633 | return value1 === undefined && value2 instanceof Error 2634 | }) 2635 | test(function() { 2636 | //Let unchecked exceptions bubble up in order to allow meaningful error messages in common cases like null reference exceptions due to typos 2637 | //An unchecked exception is defined as an object that is a subclass of Error (but not a direct instance of Error itself) - basically anything that can be thrown without an explicit `throw` keyword and that we'd never want to programmatically manipulate. In other words, an unchecked error is one where we only care about its line number and where the only reasonable way to deal with it is to change the buggy source code that caused the error to be thrown in the first place. 2638 | //By contrast, a checked exception is defined as anything that is explicitly thrown via the `throw` keyword and that can be programmatically handled, for example to display a validation error message on the UI. If an exception is a subclass of Error for whatever reason, but it is meant to be handled as a checked exception (i.e. follow the rejection rules for A+), it can be rethrown as an instance of Error 2639 | //This test tests two implementation details that differ from the Promises/A+ spec: 2640 | //1) A+ requires the `then` callback to be called in a different event loop from the resolve call, i.e. it must be asynchronous (this requires a setImmediate polyfill, which cannot be implemented in a reasonable way for Mithril's purpose - the possible polyfills are either too big or too slow) 2641 | //2) A+ swallows exceptions in a unrethrowable way, i.e. it's not possible to see default error messages on the console for runtime errors thrown from within a promise chain 2642 | var value1, value2, value3 2643 | var deferred = m.deferred() 2644 | try { 2645 | deferred.promise 2646 | .then(function(data) {foo.bar.baz}) //throws ReferenceError 2647 | .then(function(data) {value1 = 1}, function(data) {value2 = data}) 2648 | deferred.resolve("test") 2649 | } 2650 | catch (e) {value3 = e} 2651 | return value1 === undefined && value2 === undefined && (value3 instanceof ReferenceError || value3 instanceof TypeError) 2652 | }) 2653 | test(function() { 2654 | var deferred1 = m.deferred() 2655 | var deferred2 = m.deferred() 2656 | var value1, value2 2657 | deferred1.promise.then(function(data) { 2658 | value1 = data 2659 | return deferred2.promise 2660 | }).then(function(data) { 2661 | value2 = data 2662 | }) 2663 | deferred1.resolve(1) 2664 | deferred2.resolve(2) 2665 | return value1 === 1 && value2 === 2 2666 | }) 2667 | test(function() { 2668 | //https://github.com/lhorie/mithril.js/issues/80 2669 | var deferred = m.deferred(), value 2670 | deferred.resolve(1) 2671 | deferred.promise.then(function(data) { 2672 | value = data 2673 | }) 2674 | return value === 1 2675 | }) 2676 | test(function() { 2677 | //https://github.com/lhorie/mithril.js/issues/80 2678 | var deferred = m.deferred(), value 2679 | deferred.reject(1) 2680 | deferred.promise.then(null, function(data) { 2681 | value = data 2682 | }) 2683 | return value === 1 2684 | }) 2685 | test(function() { 2686 | //https://github.com/lhorie/mithril.js/issues/80 2687 | var deferred = m.deferred(), value 2688 | deferred.resolve(1) 2689 | deferred.resolve(2) 2690 | deferred.promise.then(function(data) { 2691 | value = data 2692 | }) 2693 | return value === 1 2694 | }) 2695 | test(function() { 2696 | //https://github.com/lhorie/mithril.js/issues/80 2697 | var deferred = m.deferred(), value 2698 | deferred.promise.then(function(data) { 2699 | value = data 2700 | }) 2701 | deferred.resolve(1) 2702 | deferred.resolve(2) 2703 | return value === 1 2704 | }) 2705 | test(function() { 2706 | //https://github.com/lhorie/mithril.js/issues/80 2707 | var deferred = m.deferred(), value1, value2 2708 | deferred.promise.then(function(data) { 2709 | value1 = data 2710 | }, function(data) { 2711 | value2 = data 2712 | }) 2713 | deferred.resolve(1) 2714 | deferred.reject(2) 2715 | return value1 === 1 && value2 === undefined 2716 | }) 2717 | test(function() { 2718 | //https://github.com/lhorie/mithril.js/issues/80 2719 | var deferred = m.deferred(), value1, value2 2720 | deferred.promise.then(function() { 2721 | value1 = data 2722 | }, function(data) { 2723 | value2 = data 2724 | }) 2725 | deferred.reject(1) 2726 | deferred.resolve(2) 2727 | return value1 === undefined && value2 === 1 2728 | }) 2729 | test(function() { 2730 | //https://github.com/lhorie/mithril.js/issues/80 2731 | var deferred = m.deferred(), value 2732 | deferred.promise.then(null, function(data) { 2733 | value = data 2734 | }) 2735 | deferred.reject(1) 2736 | deferred.reject(2) 2737 | return value === 1 2738 | }) 2739 | test(function() { 2740 | //https://github.com/lhorie/mithril.js/issues/85 2741 | var deferred = m.deferred(), value 2742 | deferred.resolve() 2743 | deferred.promise.then(function(data) { 2744 | value = 1 2745 | }) 2746 | return value === 1 2747 | }) 2748 | test(function() { 2749 | //https://github.com/lhorie/mithril.js/issues/85 2750 | var deferred = m.deferred(), value 2751 | deferred.reject() 2752 | deferred.promise.then(null, function(data) { 2753 | value = 1 2754 | }) 2755 | return value === 1 2756 | }) 2757 | test(function() { 2758 | var deferred = m.deferred(), value 2759 | deferred.resolve(1) 2760 | return deferred.promise() === 1 2761 | }) 2762 | test(function() { 2763 | var deferred = m.deferred(), value 2764 | var promise = deferred.promise.then(function(data) {return data + 1}) 2765 | deferred.resolve(1) 2766 | return promise() === 2 2767 | }) 2768 | test(function() { 2769 | var deferred = m.deferred(), value 2770 | deferred.reject(1) 2771 | return deferred.promise() === undefined 2772 | }) 2773 | 2774 | //m.sync 2775 | test(function() { 2776 | var value 2777 | var deferred1 = m.deferred() 2778 | var deferred2 = m.deferred() 2779 | m.sync([deferred1.promise, deferred2.promise]).then(function(data) {value = data}) 2780 | deferred1.resolve("test") 2781 | deferred2.resolve("foo") 2782 | return value[0] === "test" && value[1] === "foo" 2783 | }) 2784 | test(function() { 2785 | var value 2786 | var deferred1 = m.deferred() 2787 | var deferred2 = m.deferred() 2788 | m.sync([deferred1.promise, deferred2.promise]).then(function(data) {value = data}) 2789 | deferred2.resolve("foo") 2790 | deferred1.resolve("test") 2791 | return value[0] === "test" && value[1] === "foo" 2792 | }) 2793 | test(function() { 2794 | var value = 1 2795 | m.sync([]).then(function() {value = 2}) 2796 | return value == 2 2797 | }) 2798 | test(function() { 2799 | var success 2800 | m.sync([]).then(function(value) {success = value instanceof Array}) 2801 | return success 2802 | }) 2803 | 2804 | //m.startComputation/m.endComputation 2805 | test(function() { 2806 | mock.requestAnimationFrame.$resolve() 2807 | 2808 | var root = mock.document.createElement("div") 2809 | var controller = m.module(root, { 2810 | controller: function() {}, 2811 | view: function(ctrl) {return ctrl.value} 2812 | }) 2813 | 2814 | mock.requestAnimationFrame.$resolve() 2815 | 2816 | m.startComputation() 2817 | controller.value = "foo" 2818 | m.endComputation() 2819 | mock.requestAnimationFrame.$resolve() 2820 | return root.childNodes[0].nodeValue === "foo" 2821 | }) 2822 | 2823 | // config context 2824 | test(function() { 2825 | var root = mock.document.createElement("div") 2826 | 2827 | var success = false; 2828 | m.render(root, m("div", {config: function(elem, isInitialized, ctx) {ctx.data=1}})); 2829 | m.render(root, m("div", {config: function(elem, isInitialized, ctx) {success = ctx.data===1}})); 2830 | return success; 2831 | }) 2832 | 2833 | // more complex config context 2834 | test(function() { 2835 | var root = mock.document.createElement("div") 2836 | 2837 | var idx = 0; 2838 | var success = true; 2839 | var statefulConfig = function(elem, isInitialized, ctx) {ctx.data=idx++} 2840 | var node = m("div", {config: statefulConfig}); 2841 | m.render(root, [node, node]); 2842 | 2843 | idx = 0; 2844 | var checkConfig = function(elem, isInitialized, ctx) { 2845 | success = success && (ctx.data === idx++) 2846 | } 2847 | node = m("div", {config: checkConfig}); 2848 | m.render(root, [node, node]); 2849 | return success; 2850 | }) 2851 | 2852 | //console.log presence 2853 | test(function() { 2854 | return m.deps.factory.toString().indexOf("console") < 0 2855 | }) 2856 | test(function() { 2857 | return m.deps.factory.toString().indexOf("document.write") < 0 2858 | }) 2859 | } 2860 | 2861 | //mock 2862 | testMithril(mock.window); 2863 | 2864 | test.print(function(value) {console.log(value)}) --------------------------------------------------------------------------------