├── .gitattributes ├── .gitignore ├── Gruntfile.js ├── Knockout.WinJS.nuspec ├── LICENSE ├── README.md ├── bower.json ├── js └── knockout-winjs.js ├── karma.config.js ├── package.json ├── tasks └── check-bom.js └── tests ├── AppBar.js ├── AppBarCommand.js ├── AutoSuggestBox.js ├── BackButton.js ├── ContentDialog.js ├── DatePicker.js ├── FlipView.js ├── Flyout.js ├── Hub.js ├── HubSection.js ├── ItemContainer.js ├── ListView.js ├── ListViewLayouts.js ├── Menu.js ├── MenuCommand.js ├── Pivot.js ├── Rating.js ├── SemanticZoom.js ├── SplitView.js ├── SplitViewCommand.js ├── SplitViewPaneToggle.js ├── TestHelper.js ├── TimePicker.js ├── ToggleSwitch.js ├── ToolBar.js ├── ToolBarCommand.js └── Tooltip.js /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | [Bb]in/ 16 | [Oo]bj/ 17 | node_modules/ 18 | dist/ 19 | 20 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 21 | !packages/*/build/ 22 | 23 | # MSTest test Results 24 | [Tt]est[Rr]esult*/ 25 | [Bb]uild[Ll]og.* 26 | 27 | *_i.c 28 | *_p.c 29 | *.ilk 30 | *.meta 31 | *.obj 32 | *.pch 33 | *.pdb 34 | *.pgc 35 | *.pgd 36 | *.rsp 37 | *.sbr 38 | *.tlb 39 | *.tli 40 | *.tlh 41 | *.tmp 42 | *.tmp_proj 43 | *.log 44 | *.vspscc 45 | *.vssscc 46 | .builds 47 | *.pidb 48 | *.log 49 | *.scc 50 | 51 | # Visual C++ cache files 52 | ipch/ 53 | *.aps 54 | *.ncb 55 | *.opensdf 56 | *.sdf 57 | *.cachefile 58 | 59 | # Visual Studio profiler 60 | *.psess 61 | *.vsp 62 | *.vspx 63 | 64 | # Guidance Automation Toolkit 65 | *.gpState 66 | 67 | # ReSharper is a .NET coding add-in 68 | _ReSharper*/ 69 | *.[Rr]e[Ss]harper 70 | 71 | # TeamCity is a build add-in 72 | _TeamCity* 73 | 74 | # DotCover is a Code Coverage Tool 75 | *.dotCover 76 | 77 | # NCrunch 78 | *.ncrunch* 79 | .*crunch*.local.xml 80 | 81 | # Installshield output folder 82 | [Ee]xpress/ 83 | 84 | # DocProject is a documentation generator add-in 85 | DocProject/buildhelp/ 86 | DocProject/Help/*.HxT 87 | DocProject/Help/*.HxC 88 | DocProject/Help/*.hhc 89 | DocProject/Help/*.hhk 90 | DocProject/Help/*.hhp 91 | DocProject/Help/Html2 92 | DocProject/Help/html 93 | 94 | # Click-Once directory 95 | publish/ 96 | 97 | # Publish Web Output 98 | *.Publish.xml 99 | 100 | # NuGet Packages Directory 101 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 102 | packages/ 103 | 104 | # Windows Azure Build Output 105 | csx 106 | *.build.csdef 107 | 108 | # Windows Store app package directory 109 | AppPackages/ 110 | 111 | # Others 112 | sql/ 113 | *.Cache 114 | ClientBin/ 115 | [Ss]tyle[Cc]op.* 116 | ~$* 117 | *~ 118 | *.dbmdl 119 | *.[Pp]ublish.xml 120 | *.pfx 121 | *.publishsettings 122 | *.nupkg 123 | 124 | # RIA/Silverlight projects 125 | Generated_Code/ 126 | 127 | # Backup & report files from converting an old project file to a newer 128 | # Visual Studio version. Backup files are not needed, because we have git ;-) 129 | _UpgradeReport_Files/ 130 | Backup*/ 131 | UpgradeLog*.XML 132 | UpgradeLog*.htm 133 | 134 | # SQL Server files 135 | App_Data/*.mdf 136 | App_Data/*.ldf 137 | 138 | 139 | #LightSwitch generated files 140 | GeneratedArtifacts/ 141 | _Pvt_Extensions/ 142 | ModelManifest.xml 143 | 144 | # ========================= 145 | # Windows detritus 146 | # ========================= 147 | 148 | # Windows image file caches 149 | Thumbs.db 150 | ehthumbs.db 151 | 152 | # Folder config file 153 | Desktop.ini 154 | 155 | # Recycle Bin used on file shares 156 | $RECYCLE.BIN/ 157 | 158 | # Mac desktop service store files 159 | .DS_Store 160 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | 5 | var exec = require('child_process').exec; 6 | var execSync = require('child_process').execSync; 7 | 8 | module.exports = function(grunt) { 9 | 10 | var publishRoot = 'dist/'; 11 | var npmPublishRoot = publishRoot + 'npm/'; 12 | 13 | // All version number information is derived from package.json. This includes the version 14 | // info used with npm, bower, NuGet, and the GitHub release. 15 | var pkg = grunt.file.readJSON('package.json'); 16 | var fullWinjsVersion = pkg.devDependencies.winjs; 17 | if (!fullWinjsVersion) { 18 | grunt.fail.fatal('Unable to determine WinJS version required by knockout-winjs'); 19 | } 20 | // package.json version contains ... We just want . 21 | var winjsVersion = fullWinjsVersion.split(".").slice(0, 2).join("."); 22 | 23 | var currentGitCommitHash = execSync('git rev-parse HEAD').toString().trim(); 24 | 25 | var bomGlob = "**/*.+(js|css|htm|html)"; 26 | 27 | // Project configuration. 28 | grunt.initConfig({ 29 | pkg: pkg, 30 | 31 | clean: { 32 | publish: [publishRoot] 33 | }, 34 | 35 | copy: { 36 | publish: { 37 | files: [{ 38 | expand: true, 39 | src: [ 40 | 'js/**', 41 | 'LICENSE', 42 | 'package.json', 43 | 'README.md' 44 | ], 45 | dest: npmPublishRoot 46 | }] 47 | } 48 | }, 49 | 50 | compress: { 51 | publish: { 52 | options: { 53 | archive: publishRoot + 'knockout-winjs.zip' 54 | }, 55 | files: [{ 56 | expand: true, 57 | cwd: npmPublishRoot, 58 | src: ["**"] 59 | }] 60 | } 61 | }, 62 | 63 | "check-bom": { 64 | publish: { 65 | files: [{ 66 | cwd: "js", 67 | src: bomGlob, 68 | expand: true, 69 | nocase: true 70 | }, { 71 | cwd: publishRoot, 72 | src: bomGlob, 73 | expand: true, 74 | nocase: true 75 | }] 76 | } 77 | }, 78 | 79 | nugetpack: { 80 | publish: { 81 | src: 'Knockout.WinJS.nuspec', 82 | dest: publishRoot, 83 | options: { 84 | version: '<%= pkg.version %>' 85 | } 86 | } 87 | }, 88 | 89 | // Publishes nuget package 90 | nugetpush: { 91 | // Requires NuGet API key to be set. You can do this with: 92 | // grunt nugetkey --key=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 93 | publish: { 94 | src: publishRoot + '*.nupkg', 95 | } 96 | }, 97 | 98 | // Publishes GitHub release and bower package (bower consumes GitHub tags/releases) 99 | 'github-release': { 100 | // Requires this environment variable to be set: GITHUB_ACCESS_TOKEN 101 | // GITHUB_ACCESS_TOKEN can be generated from https://help.github.com/articles/creating-an-access-token-for-command-line-use/ 102 | publish: { 103 | options: { 104 | repository: 'winjs/knockout-winjs', 105 | auth: { 106 | user: process.env.GITHUB_ACCESS_TOKEN 107 | }, 108 | release: { 109 | tag_name: 'v<%= pkg.version %>', // Must follow semver syntax in order for bower to pick it up 110 | target_commitish: currentGitCommitHash, 111 | name: '<%= pkg.version %>', 112 | body: 113 | 'Release of knockout-winjs <%= pkg.version %>.\n' + 114 | '\n' + 115 | 'Compatible with WinJS ' + winjsVersion + '.\n' 116 | } 117 | }, 118 | files: { 119 | src: [publishRoot + 'knockout-winjs.zip'] 120 | } 121 | } 122 | } 123 | }); 124 | 125 | grunt.loadTasks('tasks/'); 126 | 127 | var plugins = [ 128 | 'grunt-contrib-clean', 129 | 'grunt-contrib-compress', 130 | 'grunt-contrib-copy', 131 | 'grunt-nuget', 132 | 'grunt-github-releaser' 133 | ]; 134 | plugins.forEach(function (plugin) { 135 | grunt.loadNpmTasks(plugin); 136 | }); 137 | 138 | // Publishes npm package 139 | grunt.registerTask('npm-release', function (mode) { 140 | var done = this.async(); 141 | var cmd = 'npm publish ' + npmPublishRoot; 142 | 143 | exec(cmd, function (err, stdout) { 144 | if (err) { 145 | grunt.fatal('npm publish failed using command: ' + cmd); 146 | } 147 | done(); 148 | }); 149 | }); 150 | 151 | // Sets up all of the state necessary to do a publish but doesn't actually publish 152 | // to any of the package managers. 153 | grunt.registerTask('prepare-publish', [ 154 | 'clean:publish', 155 | 'copy:publish', 156 | 'compress:publish', 157 | 'nugetpack:publish', 158 | 'check-bom:publish', 159 | ]); 160 | 161 | grunt.registerTask('finished-publish', function (mode) { 162 | grunt.log.writeln(''); 163 | grunt.log.writeln('Publish complete. Hand tweak the GitHub release description if necessary (https://github.com/winjs/knockout-winjs/releases)'); 164 | grunt.log.writeln(''); 165 | }); 166 | 167 | // 168 | // Public tasks designed to be run from the command line 169 | // 170 | 171 | // Populates the 'dist' folder and then uses it to: 172 | // - Create a GitHub release 173 | // - Publish to npm 174 | // - Publish to bower 175 | // - Publish to NuGet 176 | // When debugging publish, it's helpful to run just the 'prepare-publish' 177 | // task which puts all of the publication data into the 'dist' folder but 178 | // doesn't actually send the data to the package managers. 179 | grunt.registerTask('publish', function (mode) { 180 | if (!process.env.GITHUB_ACCESS_TOKEN) { 181 | grunt.fail.fatal('The GITHUB_ACCESS_TOKEN environment variable must be set in order to create GitHub releases'); 182 | } 183 | 184 | if (!mode) { 185 | grunt.log.writeln(''); 186 | grunt.log.writeln('Will publish version ' + pkg.version + ' of knockout-winjs to npm, NuGet, etc. Double check that:'); 187 | grunt.log.writeln(' * You are on the branch you\'d like to publish'); 188 | grunt.log.writeln(' * The branch has been pushed to GitHub'); 189 | grunt.log.writeln(' * You don\'t have any local edits'); 190 | grunt.log.writeln(''); 191 | grunt.log.writeln('If everything is in order, run "grunt publish:force" to proceed'); 192 | } else if (mode === 'force') { 193 | grunt.task.run([ 194 | 'prepare-publish', 195 | 'nugetpush:publish', 196 | 'github-release:publish', 197 | 'npm-release', 198 | 'finished-publish', 199 | ]); 200 | } 201 | }); 202 | }; 203 | -------------------------------------------------------------------------------- /Knockout.WinJS.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Knockout.WinJS 5 | 1.0.0 6 | Microsoft Corporation and other contributors 7 | https://raw.githubusercontent.com/winjs/knockout-winjs/master/LICENSE 8 | https://github.com/winjs/knockout-winjs 9 | false 10 | Project to smooth the Knockout/WinJS interaction 11 | winjs knockout 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Microsoft Corp. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | knockout-winjs 2 | ============= 3 | 4 | Adapter for KnockoutJS and WinJS, this code facilitates usage of WinJS UI controls in a 5 | Knockout application. It achieves this by creating bindings for the various controls 6 | which allow them to show up in Knockout data-bind attributes like: 7 | 8 | The current rating is:
9 |
10 | 11 | How to use this in your Knockout project? 12 | ---------------------------------------- 13 | 14 | Just make sure to include WinJS, and then include the adapter. 15 | 16 | 17 | 18 | 19 | 20 | 21 | __Note: this adapter library has only been tested against Knockout 3.3+.__ 22 | 23 | Examples of control usage 24 | ------------------------- 25 | 26 | ### AppBar and AppBarCommand 27 | 28 | 29 |
30 | 31 |
32 | 33 | ### AutoSuggestBox 34 | 35 |
36 | 37 | ### BackButton 38 | 39 | 40 | 41 | 42 | ### ContentDialog 43 | 44 |
45 | 46 | ### DatePicker 47 | 48 |
49 | 50 | ### FlipView 51 | 52 |
53 | 61 | 62 | ### Flyout 63 | 64 | 65 |
66 |
This is a flyout
67 |
68 | 69 | ### Hub and HubSection 70 | 71 |
72 |
73 |
74 |
75 |
76 | 77 | ### ItemContainer 78 | 79 | 80 |
81 |
82 |
Person:   83 | Rating:   84 | Selected:
85 |
86 |
87 | 88 | ### ListView 89 | 90 |
91 | 101 | 102 | ### Menu and MenuCommand 103 | 104 | 105 |
106 | 107 |
108 | 109 |
110 | 111 | ### Pivot 112 | 113 |
114 |
Item1
115 |
Item2
116 |
117 | 118 | ### Rating 119 | 120 | The current rating is:
121 |
122 | 123 | ### SemanticZoom 124 | 125 |
126 |
127 |
128 |
129 | 130 | ### SplitView 131 | 132 |
133 |
134 | SplitView pane area 135 | 136 | 137 |
138 |
Text for the SplitView content area
139 |
More text for the SplitView content area
140 |
141 | 142 | ### SplitViewPaneToggle 143 | 144 | 145 | 146 | ### TimePicker 147 | 148 |
149 | 150 | ### ToggleSwitch 151 | 152 |
153 | 154 | ### ToolBar 155 | 156 |
157 | 158 | 159 |
160 | 161 | ### Tooltip 162 | 163 |
164 |
Hover for infoTip
165 |
166 |
167 |
168 |
Tooltip only allows one time binding of with contentElement:
169 |
170 |
171 |
172 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "knockout-winjs", 3 | "main": "js/knockout-winjs.js", 4 | "homepage": "https://github.com/winjs/knockout-winjs", 5 | "authors": [ 6 | "Microsoft Corporation and other contributors" 7 | ], 8 | "description": "Project to smooth the Knockout/WinJS interaction", 9 | "keywords": [ 10 | "WinJS", 11 | "knockout" 12 | ], 13 | "license": "MIT", 14 | "ignore": [ 15 | "**/.*", 16 | "node_modules", 17 | "bower_components", 18 | "test", 19 | "tests", 20 | "bin", 21 | "dist", 22 | "package.json", 23 | "*.nuspec", 24 | "Gruntfile.js" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /js/knockout-winjs.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | (function () { 4 | "use strict"; 5 | 6 | // A property processor should return this if the intention is that the return value is not to be set on the control property 7 | var doNotSetOptionOnControl = {}; 8 | 9 | // Check if property starts with 'on' to see if it's an event handler 10 | function isPropertyEventHandler(propertyName) { 11 | return propertyName[0] === "o" && propertyName[1] === "n"; 12 | } 13 | 14 | // Check if two WinJS layouts are considered the same 15 | function isSameLayout(layout1, layout2) { 16 | if (layout1 && layout2 && layout1.type && layout2.type) { 17 | return objectShallowEquals(layout1, layout2); 18 | } else { 19 | return layout1 === layout2; 20 | } 21 | }; 22 | 23 | function arrayShallowEquals(array1, array2) { 24 | if (array1 === array2) { 25 | return true; 26 | } 27 | if (!array1 || !array2 || array1.length !== array2.length) { 28 | return false; 29 | } 30 | for (var i in array1) { 31 | if (array2[i] !== array1[i]) { 32 | return false; 33 | } 34 | } 35 | return true; 36 | } 37 | 38 | // Perform a shallow comparison of two objects 39 | function objectShallowEquals(object1, object2) { 40 | if (object1 === object2) { 41 | return true; 42 | } 43 | for (var prop in object1) { 44 | if (object1[prop] !== object2[prop]) { 45 | return false; 46 | } 47 | } 48 | for (var prop in object2) { 49 | if (object1[prop] !== object2[prop]) { 50 | return false; 51 | } 52 | } 53 | return true; 54 | } 55 | 56 | function addBindings(controls, eventConfig) { 57 | Object.keys(controls).forEach(function (name) { 58 | var controlConfiguration = controls[name]; 59 | var eventsChangingProperties = eventConfig[name]; 60 | var ctor = WinJS.Utilities.getMember("WinJS.UI." + name); 61 | var propertyProcessor = controlConfiguration.propertyProcessor || {}; 62 | var delayedPropertyProcessor = controlConfiguration.postCtorPropertyProcessor || {}; 63 | var bindDescendantsBeforeParent = controlConfiguration.bindDescendantsBeforeParent || false; 64 | var bindingName = "win" + name; 65 | 66 | ko.bindingHandlers[bindingName] = { 67 | init: function (element, valueAccessor, allBindings, viewModel, bindingContext) { 68 | 69 | // The options for the control 70 | var value = valueAccessor(); 71 | 72 | // Options record for the WinJS Control 73 | var options = {}; 74 | 75 | // If the WinJS control depends on having child elements 76 | if (element.children.length > 0 && bindDescendantsBeforeParent) { 77 | ko.applyBindingsToDescendants(bindingContext, element); 78 | } 79 | 80 | // Iterate over the observable properties to get their value 81 | for (var property in value) { 82 | // Exclude events from options to set up during ctor, we explicitly set event handlers in the update function 83 | // because setting an event property will not unhook the event listener that was set as options during initialization. 84 | if (value.hasOwnProperty(property) && !isPropertyEventHandler(property) && (!delayedPropertyProcessor[property])) { 85 | if (propertyProcessor[property]) { 86 | var propertyResult = propertyProcessor[property](value[property], function () { return element }); 87 | 88 | // doNotSetOptionOnControl from propertyProcessor means don't set the option on the control 89 | if (propertyResult !== doNotSetOptionOnControl) { 90 | options[property] = propertyResult; 91 | } 92 | } else { 93 | options[property] = ko.unwrap(value[property]); 94 | } 95 | } 96 | } 97 | 98 | // Create a new instance of the control with the element and options 99 | var control = new ctor(element, options); 100 | 101 | // Add event handler that will kick off changes to the changing properties that are observed 102 | if (eventConfig[name]) { 103 | var events = eventConfig[name]; 104 | for (var event in events) { 105 | ko.utils.registerEventHandler(element, event, function changed(e) { 106 | 107 | // Iterate over the properties that change as a result of the events 108 | for (var propertyIndex in eventConfig[name][event]) { 109 | var property = eventConfig[name][event][propertyIndex]; 110 | // Check to see if they exist 111 | if (value && value.hasOwnProperty(property)) { 112 | // Determine if that value is a writableObservable property and check the property has changed 113 | if (ko.isWriteableObservable(value[property]) && value[property]() !== control[property]) { 114 | // Kickoff updates 115 | value[property](control[property]); 116 | } 117 | } 118 | } 119 | }); 120 | } 121 | } 122 | 123 | // Add disposal callback to dispose the WinJS control when it's not needed anymore 124 | ko.utils.domNodeDisposal.addDisposeCallback(element, function (e) { 125 | if (element.winControl) { 126 | element.winControl.dispose(); 127 | } 128 | }); 129 | 130 | return { controlsDescendantBindings: bindDescendantsBeforeParent }; 131 | }, 132 | 133 | update: function (element, valueAccessor, allBindings, viewModel, bindingContext) { 134 | // Get the WinJS control 135 | var control = element.winControl; 136 | var value = valueAccessor(); 137 | 138 | // Only update the control properties that are different with the unpacked value, we also process delayed property processors only here 139 | for (var property in value) { 140 | if (value.hasOwnProperty(property)) { 141 | var unwrappedValue = ko.unwrap(value[property]); 142 | if (control[property] !== unwrappedValue) { 143 | if (propertyProcessor && propertyProcessor[property]) { 144 | var returnValue = propertyProcessor[property](value[property], function () { return element }, control[property]); 145 | if (returnValue !== doNotSetOptionOnControl) { 146 | control[property] = returnValue; 147 | } 148 | } else if (delayedPropertyProcessor && delayedPropertyProcessor[property]) { 149 | var returnValue = delayedPropertyProcessor[property](value[property], function () { return element }, control[property]); 150 | if (returnValue !== doNotSetOptionOnControl) { 151 | control[property] = returnValue; 152 | } 153 | } else { 154 | control[property] = unwrappedValue; 155 | } 156 | } 157 | } 158 | } 159 | } 160 | } 161 | }); 162 | } 163 | 164 | // Helper for diffing between an obserable array and binding list 165 | function bindingListWatch(value, oldValue, sourceElement) { 166 | var unpacked = ko.unwrap(value); 167 | // Will create a bindingList once per new observable array 168 | var retVal = doNotSetOptionOnControl; 169 | if (Array.isArray(unpacked) && ko.isWriteableObservable(value)) { 170 | if (!value._winKoChangesSubscription) { 171 | if (value._rateLimitedChange) { 172 | throw new Error("Knockout-WinJS does not support rate limited observable arrays currently"); 173 | } 174 | var bindingList = new WinJS.Binding.List(unpacked); 175 | 176 | value._winKoChangesSubscriptionDisabled = false; 177 | value._winKoBindingList = bindingList; 178 | 179 | // Subscribe to the array diff callback for observable array changes 180 | value._winKoChangesSubscription = value.subscribe(function (newValue) { 181 | if (!value._winKoChangesSubscriptionDisabled) { 182 | 183 | // disable binding list callbacks to prevent an infinte loop 184 | bindingList._winKoChangesSubscriptionDisabled = true; 185 | var offset = 0; 186 | 187 | var deletes = newValue.filter(function (item) { 188 | return item.status === "deleted"; 189 | }); 190 | var adds = newValue.filter(function (item) { 191 | return item.status === "added"; 192 | }); 193 | for (var deletedItem in deletes) { 194 | var item = deletes[deletedItem]; 195 | bindingList.splice(item.index - offset, 1); 196 | offset++; 197 | } 198 | 199 | var arrayLength = bindingList.length; 200 | for (var i = 0; i < adds.length; i++) { 201 | var item = adds[i]; 202 | if (item.index === arrayLength) { 203 | bindingList.push(item.value); 204 | } else if (item.index === 0) { 205 | bindingList.unshift(item.value); 206 | } else { 207 | bindingList.push(item.value) 208 | bindingList.move(arrayLength, item.index); 209 | } 210 | arrayLength++; 211 | } 212 | bindingList._winKoChangesSubscriptionDisabled = false; 213 | } 214 | }, this, "arrayChange"); 215 | 216 | // Binding list may also change on its accord (i.e. ListView reorder) for which we should update the observable array 217 | var bindingListMutationEvents = ["itemchanged", "itemmoved", "itemmutated", "itemremoved", "reload"]; 218 | 219 | var updateOriginalArray = function () { 220 | if (!bindingList._winKoChangesSubscriptionDisabled) { 221 | 222 | // Disable observable array callbacks to prevent an infinite loop 223 | value._winKoChangesSubscriptionDisabled = true; 224 | value.removeAll(); 225 | for (var i = 0, len = bindingList.length; i < len; i++) { 226 | value.push(bindingList.getAt(i)); 227 | } 228 | value._winKoChangesSubscriptionDisabled = false; 229 | } 230 | }; 231 | 232 | bindingListMutationEvents.forEach(function (event) { 233 | bindingList.addEventListener(event, updateOriginalArray); 234 | }); 235 | 236 | // Dispose the old subscription or when the element gets disposed 237 | ko.utils.domNodeDisposal.addDisposeCallback(sourceElement(), function () { 238 | value._winKoChangesSubscription.dispose(); 239 | }); 240 | 241 | } 242 | // Only return the binding list data source if it was not set priorly 243 | if (!sourceElement()._winKoDataSourceBound) { 244 | sourceElement()._winKoDataSourceBound = true; 245 | retVal = value._winKoBindingList.dataSource; 246 | } 247 | } else { 248 | retVal = unpacked; 249 | } 250 | 251 | return retVal; 252 | } 253 | 254 | // Helper for itemTemplate changes 255 | function itemTemplateWatch(value, oldValue, sourceElement, property) { 256 | var retVal = doNotSetOptionOnControl; 257 | value = ko.unwrap(value); 258 | var template = value; 259 | var renderer; 260 | 261 | sourceElement = sourceElement(); 262 | // If the renderer is a string that means we are trying to render a KO template 263 | if (typeof value === "string") { 264 | renderer = WinJS.UI.simpleItemRenderer(function (item) { 265 | var element = document.createElement("div"); 266 | ko.renderTemplate(template, item.data, {}, element); 267 | return element; 268 | }); 269 | } else { 270 | // Otherwise the template is a WinJS function template 271 | renderer = value; 272 | } 273 | var templateProp = "win" + property + "Old"; 274 | if (!oldValue || template !== sourceElement[templateProp]) { 275 | sourceElement[templateProp] = template; 276 | retVal = renderer; 277 | } 278 | 279 | return retVal; 280 | } 281 | 282 | var controls = { 283 | // We need to bind child nodes of AppBar (buttons) before AppBar itself in knockout 284 | AppBar: { 285 | bindDescendantsBeforeParent: true 286 | }, 287 | AppBarCommand: { 288 | propertyProcessor: { 289 | 'type': function (value, appBarCommandElement, update) { 290 | if (!appBarCommandElement._winTypeInitialized) { 291 | appBarCommandElement._winTypeInitialized = true; 292 | return value; 293 | } else { 294 | console.warn("Cannot change AppBarCommand type after initializing the control"); 295 | } 296 | } 297 | } 298 | }, 299 | AutoSuggestBox: {}, 300 | BackButton: {}, 301 | Command: { 302 | propertyProcessor: { 303 | 'type': function (value, commandElement, update) { 304 | if (!commandElement._winTypeInitialized) { 305 | commandElement._winTypeInitialized = true; 306 | return value; 307 | } else { 308 | console.warn("Cannot change Command type after initializing the control"); 309 | } 310 | } 311 | } 312 | }, 313 | ContentDialog: {}, 314 | DatePicker: {}, 315 | FlipView: { 316 | propertyProcessor: { 317 | 'itemTemplate': function (value, flipViewElement, current) { 318 | return itemTemplateWatch(value, current, flipViewElement, 'ItemTemplate'); 319 | }, 320 | 'itemDataSource': function (value, flipViewElement, current) { 321 | return bindingListWatch(value, current, flipViewElement); 322 | } 323 | }, 324 | bindDescendantsBeforeParent: true 325 | }, 326 | Flyout: {}, 327 | Hub: { 328 | bindDescendantsBeforeParent: true, 329 | }, 330 | HubSection: {}, 331 | ItemContainer: {}, 332 | ListView: { 333 | propertyProcessor: { 334 | 'groupHeaderTemplate': function (value, listViewElement, current) { 335 | return itemTemplateWatch(value, current, listViewElement, 'GroupHeaderTemplate'); 336 | }, 337 | 'groupDataSource': function (value, listViewElement, current) { 338 | return bindingListWatch(value, current, listViewElement); 339 | }, 340 | 'itemTemplate': function (value, listViewElement, current) { 341 | return itemTemplateWatch(value, current, listViewElement, 'ItemTemplate'); 342 | }, 343 | 'itemDataSource': function (value, listViewElement, current) { 344 | return bindingListWatch(value, current, listViewElement); 345 | }, 346 | 'layout': function (value, listViewElement, current) { 347 | var retVal = doNotSetOptionOnControl; 348 | var unpacked = ko.unwrap(value); 349 | var listViewElement = listViewElement(); 350 | 351 | // Only set layout if it's an actually different one 352 | if (!current || !isSameLayout(unpacked, listViewElement._winCachedLayout)) { 353 | retVal = (unpacked && unpacked.type) ? new unpacked.type(unpacked) : unpacked; 354 | listViewElement._winCachedLayout = unpacked; 355 | } 356 | 357 | return retVal; 358 | } 359 | }, 360 | postCtorPropertyProcessor: { 361 | // Selection needs to set selection object on the list view and needs the control to be initialized 362 | 'selection': function (value, listViewElement, current) { 363 | var unpacked = ko.unwrap(value); 364 | listViewElement = listViewElement(); 365 | 366 | // If value is a ko.observableArray, subscribe to selection changes on it 367 | if (Array.isArray(unpacked) && ko.isWriteableObservable(value)) { 368 | if (!listViewElement._winKoSelectionChangedHandlerSet) { 369 | listViewElement.winControl.addEventListener("selectionchanged", function () { 370 | var currSelectionArray = listViewElement.winControl.selection.getIndices(); 371 | var oldSelection = ko.unwrap(value); 372 | if (!arrayShallowEquals(oldSelection, currSelectionArray)) { 373 | value(listViewElement.winControl.selection.getIndices()); 374 | } 375 | }); 376 | listViewElement._winKoSelectionChangedHandlerSet = true; 377 | } 378 | } 379 | listViewElement.winControl.selection.set(unpacked); 380 | 381 | return doNotSetOptionOnControl; 382 | } 383 | }, 384 | bindDescendantsBeforeParent: true 385 | }, 386 | Menu: { 387 | bindDescendantsBeforeParent: true 388 | }, 389 | MenuCommand: {}, 390 | Pivot: { 391 | bindDescendantsBeforeParent: true, 392 | propertyProcessor: { 393 | 'selectedIndex': function (value, pivotElement, current) { 394 | // Temporary workaround for selectionchanged not updating this property until WinJS issue #1317 is fixed 395 | if (!pivotElement._winKoSelectedIndexHandlerSet) { 396 | pivotElement().addEventListener("selectionchanged", function (e) { 397 | if (ko.isWriteableObservable(value)) { 398 | value(e.detail.index); 399 | } 400 | }); 401 | pivotElement()._winKoSelectedIndexHandlerSet = true; 402 | } 403 | return ko.unwrap(value); 404 | }, 405 | 'selectedItem': function (value, pivotElement, current) { 406 | // Temporary workaround for selectionchanged not updating this property until WinJS issue #1317 is fixed 407 | if (!pivotElement._winKoSelectedItemHandlerSet) { 408 | pivotElement().addEventListener("selectionchanged", function (e) { 409 | if (ko.isWriteableObservable(value)) { 410 | value(pivotElement().winControl.items.getAt(e.detail.index)); 411 | } 412 | }); 413 | pivotElement()._winKoSelectedItemHandlerSet = true; 414 | } 415 | return ko.unwrap(value); 416 | }, 417 | } 418 | }, 419 | PivotItem: {}, 420 | Rating: {}, 421 | SemanticZoom: { 422 | bindDescendantsBeforeParent: true 423 | }, 424 | SplitView: {}, 425 | SplitViewCommand: {}, 426 | SplitViewPaneToggle: {}, 427 | TimePicker: {}, 428 | ToggleSwitch: {}, 429 | ToolBar: { 430 | bindDescendantsBeforeParent: true 431 | }, 432 | Tooltip: { 433 | propertyProcessor: { 434 | 'contentElement': function (value, toolTipElement, current) { 435 | var value = ko.unwrap(value); 436 | var element = document.querySelector(value); 437 | return element; 438 | } 439 | } 440 | }, 441 | ViewBox: {} 442 | }; 443 | 444 | // An object to store which event of a control changes what property 445 | var eventConfig = { 446 | AppBar: { 447 | "afterclose": ["opened"], 448 | "afteropen": ["opened"] 449 | }, 450 | AutoSuggestBox: { 451 | "querychanged": ["queryText"] 452 | }, 453 | ContentDialog: { 454 | "afterhide": ["hidden"], 455 | "aftershow": ["hidden"] 456 | }, 457 | DatePicker: { 458 | "change": ["current"] 459 | }, 460 | FlipView: { 461 | "pageselected": ["currentPage"] 462 | }, 463 | Flyout: { 464 | "afterhide": ["hidden"], 465 | "aftershow": ["hidden"] 466 | }, 467 | Hub: { 468 | "loadingstatechanged": ["loadingState"] 469 | }, 470 | ItemContainer: { 471 | "selectionchanged": ["selected"] 472 | }, 473 | ListView: { 474 | "loadingstatechanged": ["loadingState"] 475 | }, 476 | Menu: { 477 | "afterhide": ["hidden"], 478 | "aftershow": ["hidden"] 479 | }, 480 | NavBar: { 481 | "afterclose": ["opened"], 482 | "afteropen": ["opened"] 483 | }, 484 | Pivot: null, 485 | // "selectionchanged": ["selectedIndex", "selectedItem"] Need a custom hook for selectionchanged 486 | // until WinJS #1317 Pivot selectedItem and selectedIndex are not updated on selectionchanged handler is fixed 487 | // }, 488 | Rating: { 489 | "change": ["userRating"] 490 | }, 491 | SemanticZoom: { 492 | "zoomchanged": ["zoomedOut"] 493 | }, 494 | SplitView: { 495 | "afterclose": ["paneOpened"], 496 | "afteropen": ["paneOpened"] 497 | }, 498 | TimePicker: { 499 | "change": ["current"] 500 | }, 501 | ToggleSwitch: { 502 | "change": ["checked"] 503 | }, 504 | ToolBar: { 505 | "afterclose": ["opened"], 506 | "afteropen": ["opened"] 507 | } 508 | }; 509 | 510 | addBindings(controls, eventConfig); 511 | 512 | })(); -------------------------------------------------------------------------------- /karma.config.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Thu Dec 04 2014 11:26:12 GMT-0800 (Pacific Standard Time) 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | 7 | // base path, that will be used to resolve files and exclude 8 | basePath: '', 9 | 10 | 11 | // frameworks to use 12 | frameworks: ['jasmine'], 13 | 14 | 15 | // list of files / patterns to load in the browser 16 | files: [ 17 | 'node_modules/winjs/js/base.js', 18 | 'node_modules/winjs/js/ui.js', 19 | 'node_modules/winjs/css/ui-dark.css', 20 | "node_modules/knockout/build/output/knockout-latest.debug.js", 21 | 'js/knockout-winjs.js', 22 | 'tests/TestHelper.js', // Make sure this is loaded first 23 | 'tests/*.js', 24 | ], 25 | 26 | 27 | // list of files to exclude 28 | exclude: [ 29 | 30 | ], 31 | 32 | 33 | // test results reporter to use 34 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage' 35 | reporters: ['progress'], 36 | 37 | 38 | // web server port 39 | port: 9876, 40 | 41 | 42 | // enable / disable colors in the output (reporters and logs) 43 | colors: true, 44 | 45 | 46 | // level of logging 47 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 48 | logLevel: config.LOG_INFO, 49 | 50 | 51 | // enable / disable watching file and executing tests whenever any file changes 52 | autoWatch: true, 53 | 54 | 55 | // Start these browsers, currently available: 56 | // - Chrome 57 | // - ChromeCanary 58 | // - Firefox 59 | // - Opera (has to be installed with `npm install karma-opera-launcher`) 60 | // - Safari (only Mac; has to be installed with `npm install karma-safari-launcher`) 61 | // - PhantomJS 62 | // - IE (only Windows; has to be installed with `npm install karma-ie-launcher`) 63 | browsers: ['Chrome', 'Firefox', 'IE'], 64 | 65 | 66 | // If browser does not capture in given timeout [ms], kill it 67 | captureTimeout: 60000, 68 | 69 | 70 | // Continuous Integration mode 71 | // if true, it capture browsers, run tests and exit 72 | singleRun: false 73 | }); 74 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "knockout-winjs", 3 | "version": "2.4.0", 4 | "description": "Project to smooth the Knockout/WinJS interaction", 5 | "main": "js/knockout-winjs.js", 6 | "scripts": { 7 | "test": "karma start karma.config.js" 8 | }, 9 | "directories": { 10 | "test": "test" 11 | }, 12 | "keywords": [ 13 | "WinJS", 14 | "knockout" 15 | ], 16 | "author": "Microsoft Corporation and other contributors", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/winjs/knockout-winjs.git" 20 | }, 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/winjs/knockout-winjs/issues" 24 | }, 25 | "homepage": "https://github.com/winjs/knockout-winjs", 26 | "devDependencies": { 27 | "jasmine-core": "^2.3.4", 28 | "karma": "^0.12.37", 29 | "karma-chrome-launcher": "^0.2.0", 30 | "karma-jasmine": "^0.3.6", 31 | "grunt": "^0.4.5", 32 | "grunt-contrib-clean": "^0.6.0", 33 | "grunt-contrib-compress": "^0.13.0", 34 | "grunt-contrib-copy": "^0.8.0", 35 | "grunt-github-releaser": "^0.1.17", 36 | "grunt-nuget": "^0.1.4", 37 | "knockout": "^3.3.0", 38 | "winjs": "4.4.x" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tasks/check-bom.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | (function () { 3 | "use strict"; 4 | 5 | module.exports = function (grunt) { 6 | 7 | // Verifies that files begin with a UTF8 BOM. Files without one will not be able to pass the 8 | // Windows App Certification Kit test. 9 | 10 | grunt.registerMultiTask("check-bom", function () { 11 | function checkBom(filePath) { 12 | if (grunt.file.exists(filePath)) { 13 | var content = grunt.file.read(filePath, { encoding: null }); 14 | if (content.length < 3 || content[0] !== 0xef || content[1] !== 0xbb || content[2] !== 0xbf) { 15 | grunt.fail.fatal("check-bom File is missing BOM: " + filePath); 16 | } 17 | } else { 18 | grunt.log.warn("check-bom No such file: " + filePath); 19 | } 20 | } 21 | 22 | this.filesSrc.forEach(checkBom); 23 | }); 24 | 25 | }; 26 | })(); 27 | -------------------------------------------------------------------------------- /tests/AppBar.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("AppBar control directive tests", function () { 5 | var testTimeout = 5000 6 | 7 | sharedSetup(); 8 | enableFastAnimations(); 9 | extendTimeout(); 10 | 11 | it("should initialize a simple AppBar", function () { 12 | var simpleAppBar = initControl("
"); 13 | 14 | expect(simpleAppBar.winControl).toBeDefined(); 15 | expect(simpleAppBar.winControl instanceof WinJS.UI.AppBar); 16 | expect(simpleAppBar.className).toContain("win-appbar"); 17 | }); 18 | 19 | it("should use child AppBarCommands", function () { 20 | var compiledControl = initControl("
" + 21 | "" + 22 | "" + 23 | "
"); 24 | 25 | expect(compiledControl.winControl).toBeDefined(); 26 | expect(compiledControl.winControl instanceof WinJS.UI.AppBar); 27 | expect(compiledControl.className).toContain("win-appbar"); 28 | expect(compiledControl.querySelectorAll(".win-command").length).toEqual(2); 29 | }); 30 | 31 | it("should use the data attribute", function () { 32 | var ControlModel = function () { 33 | this.testCommands = new WinJS.Binding.List([ 34 | new WinJS.UI.AppBarCommand(null, { label: "TestCommand0" }), 35 | new WinJS.UI.AppBarCommand(null, { label: "TestCommand1" }) 36 | ]); 37 | }; 38 | var compiledControl = initControl("
", new ControlModel()); 39 | var commands = compiledControl.querySelectorAll(".win-command"); 40 | 41 | expect(commands.length).toEqual(2); 42 | expect(commands[0].querySelector(".win-label").innerHTML).toEqual("TestCommand0"); 43 | expect(commands[1].querySelector(".win-label").innerHTML).toEqual("TestCommand1") 44 | }); 45 | 46 | it("should use the closedDisplayMode attribute", function () { 47 | var compiledControl = initControl("
"); 48 | 49 | expect(compiledControl.winControl.closedDisplayMode).toEqual("minimal"); 50 | }); 51 | 52 | it("should use the placement attribute", function () { 53 | var compiledControl = initControl("
"); 54 | 55 | expect(compiledControl.winControl.placement).toEqual("top"); 56 | }); 57 | 58 | it("should use the opened attribute", function () { 59 | var compiledControl = initControl("
"); 60 | 61 | expect(compiledControl.winControl.opened).toBeTruthy(); 62 | }); 63 | 64 | it("should use the onopen event handlers and opened attribute", function (done) { 65 | testEventChangesProperty({ 66 | control: "AppBar", 67 | beforeChangeEvent: "beforeopen", 68 | afterChangeEvent: "afteropen", 69 | property: "opened", 70 | initialValue: false, 71 | finalValue: true 72 | }).then(function () { 73 | done(); 74 | }); 75 | }); 76 | 77 | it("should use the onclose event handlers and opened attribute", function (done) { 78 | testEventChangesProperty({ 79 | control: "AppBar", 80 | beforeChangeEvent: "beforeclose", 81 | afterChangeEvent: "afterclose", 82 | property: "opened", 83 | initialValue: true, 84 | finalValue: false 85 | }).then(function () { 86 | done(); 87 | }); 88 | }); 89 | 90 | }); -------------------------------------------------------------------------------- /tests/AppBarCommand.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("AppBarCommand control directive tests", function () { 5 | 6 | sharedSetup(); 7 | 8 | it("should use initialize an AppBar containing two child AppBarCommands", function () { 9 | var compiledControl = initControl("
" + 10 | "" + 11 | "" + 12 | "
"); 13 | var appBarCommand = compiledControl.childNodes[0]; 14 | 15 | expect(appBarCommand.winControl).toBeDefined(); 16 | expect(appBarCommand.winControl instanceof WinJS.UI.AppBarCommand); 17 | expect(appBarCommand.className).toContain("win-command"); 18 | expect(compiledControl.querySelectorAll(".win-command").length).toEqual(2); 19 | }); 20 | 21 | it("should use the id attribute on AppBarCommands", function () { 22 | var compiledControl = initControl("
" + 23 | "" + 24 | "" + 25 | "
"); 26 | var commands = compiledControl.querySelectorAll(".win-command"); 27 | 28 | expect(commands[0].id).toEqual("command1"); 29 | expect(commands[1].id).toEqual("command2"); 30 | }); 31 | 32 | it("should use the label attribute on AppBarCommands", function () { 33 | var compiledControl = initControl("
" + 34 | "" + 35 | "" + 36 | "
"); 37 | var commands = compiledControl.querySelectorAll(".win-command"); 38 | 39 | expect(commands[0].querySelector(".win-label").innerHTML).toEqual("command1"); 40 | expect(commands[1].querySelector(".win-label").innerHTML).toEqual("command2"); 41 | }); 42 | 43 | it("should use the disabled attribute on AppBarCommands", function () { 44 | var compiledControl = initControl("
" + 45 | "" + 46 | "" + 47 | "
"); 48 | var commands = compiledControl.querySelectorAll(".win-command"); 49 | 50 | expect(commands[0].winControl.disabled).toBeTruthy(); 51 | expect(commands[1].winControl.disabled).toBeFalsy(); 52 | }); 53 | 54 | it("should use the extraClass attribute on AppBarCommands", function () { 55 | var compiledControl = initControl("
" + 56 | "" + 57 | "
"); 58 | var commands = compiledControl.querySelectorAll(".win-command"); 59 | 60 | expect(commands[0].className).toContain("extraClass1"); 61 | }); 62 | 63 | it("should use the section attribute on AppBarCommands", function () { 64 | var compiledControl = initControl("
" + 65 | "" + 66 | "" + 67 | "
"); 68 | var commands = compiledControl.querySelectorAll(".win-command"); 69 | 70 | expect(commands[0].winControl.section).toEqual("primary"); 71 | expect(commands[1].winControl.section).toEqual("secondary"); 72 | }); 73 | 74 | it("should use the hidden attribute on AppBarCommands", function () { 75 | var compiledControl = initControl("
" + 76 | "" + 77 | "" + 78 | "
"); 79 | var commands = compiledControl.querySelectorAll(".win-command"); 80 | 81 | expect(commands[0].winControl.hidden).toBeTruthy(); 82 | expect(commands[1].winControl.hidden).toBeFalsy(); 83 | }); 84 | 85 | it("should use the icon attribute on AppBarCommands", function () { 86 | var compiledControl = initControl("
" + 87 | "" + 88 | "
"); 89 | var commandImage = compiledControl.querySelector(".win-commandimage"); 90 | 91 | expect(escape(commandImage.innerHTML)).toEqual("%uE109"); 92 | }); 93 | 94 | it("should use the win-app-bar command types", function () { 95 | var compiledControl = initControl("
" + 96 | "
" + 97 | "
" + 98 | "" + 99 | "
"); 100 | var commands = compiledControl.querySelectorAll(".win-command"); 101 | 102 | expect(commands[0].winControl.type).toEqual("separator"); 103 | expect(commands[1].winControl.type).toEqual("content"); 104 | expect(commands[2].winControl.type).toEqual("toggle"); 105 | }); 106 | 107 | it("should use the priority attribute", function () { 108 | var compiledControl = initControl("
" + 109 | "" + 110 | "" + 111 | "
"); 112 | var commands = compiledControl.querySelectorAll(".win-command"); 113 | 114 | expect(commands[0].winControl.priority).toEqual(1); 115 | expect(commands[1].winControl.priority).toEqual(2); 116 | }); 117 | 118 | it("should use the flyout attribute", function () { 119 | var ControlModel = function () { 120 | this.flyout = new WinJS.UI.Flyout(); 121 | }; 122 | var model = new ControlModel(); 123 | var compiledControl = initControl("
" + 124 | "" + 125 | "
", model); 126 | var commands = compiledControl.querySelectorAll(".win-command"); 127 | 128 | expect(commands[0].winControl.flyout).toEqual(model.flyout); 129 | }); 130 | }); -------------------------------------------------------------------------------- /tests/AutoSuggestBox.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("AutoSuggestBox control directive tests", function () { 5 | 6 | sharedSetup(); 7 | 8 | it("should initialize a simple autosuggestbox", function () { 9 | var compiledControl = initControl("
"); 10 | 11 | expect(compiledControl.winControl).toBeDefined(); 12 | expect(compiledControl.winControl instanceof WinJS.UI.AutoSuggestBox); 13 | expect(compiledControl.className).toContain("win-autosuggestbox"); 14 | }); 15 | 16 | it("should use the chooseSuggestionOnEnter attribute", function () { 17 | var compiledControl = initControl("
"); 18 | 19 | expect(compiledControl.winControl.chooseSuggestionOnEnter).toBeTruthy(); 20 | }); 21 | 22 | it("should use the placeholderText attribute", function () { 23 | var compiledControl = initControl("
"); 24 | 25 | expect(compiledControl.winControl.placeholderText).toEqual("Some Placeholder Text"); 26 | }); 27 | 28 | it("should use the queryText attribute", function () { 29 | var compiledControl = initControl("
"); 30 | 31 | expect(compiledControl.winControl.queryText).toEqual("Some Query Text"); 32 | }); 33 | 34 | // TODO: Enable after AutoSuggestBox does not fire onquerychanged when queryText is programmatically updated #1293 is fixed 35 | xit("should bind the queryText attribute to observable", function () { 36 | var ControlModel = function () { 37 | this.textObservable = ko.observable("Query1"); 38 | }; 39 | var model = new ControlModel(); 40 | var compiledControl = initControl("
", model); 41 | 42 | expect(compiledControl.winControl.queryText).toEqual("Query1"); 43 | compiledControl.winControl.queryText = "Query2"; 44 | expect(model.textObservable()).toEqual("Query2"); 45 | }); 46 | 47 | it("should use the searchHistoryContext attribute", function () { 48 | var compiledControl = initControl("
"); 49 | 50 | expect(compiledControl.winControl.searchHistoryContext).toEqual("searchContext"); 51 | }); 52 | 53 | it("should use the searchHistoryDisabled attribute", function () { 54 | var compiledControl = initControl("
"); 55 | 56 | expect(compiledControl.winControl.searchHistoryDisabled).toBeTruthy(); 57 | }); 58 | 59 | // TODO: Enable after AutoSuggestBox does not fire onquerychanged when queryText is programmatically updated #1293 is fixed 60 | xit("should use the querychanged event", function (done) { 61 | var ControlModel = function () { 62 | this.textObservable = "originalQuery"; 63 | this.changedQuery = function () { 64 | expect(this.textObservable()).toEqual("changedQuery"); 65 | done(); 66 | } 67 | }; 68 | var model = new ControlModel(); 69 | var compiledControl = initControl("
<\div>", model); 70 | 71 | compiledControl.winControl.queryText = "changedQuery"; 72 | }); 73 | 74 | }); -------------------------------------------------------------------------------- /tests/BackButton.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("BackButton control directive tests", function () { 5 | 6 | sharedSetup(); 7 | 8 | it("should initialize a simple BackButton", function () { 9 | var compiledControl = initControl(""); 10 | expect(compiledControl.winControl).toBeDefined(); 11 | expect(compiledControl.winControl instanceof WinJS.UI.BackButton); 12 | expect(compiledControl.className).toContain("win-navigation-backbutton"); 13 | }); 14 | 15 | }); -------------------------------------------------------------------------------- /tests/ContentDialog.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("ContentDialog control directive tests", function () { 5 | 6 | sharedSetup(); 7 | enableFastAnimations(); 8 | extendTimeout(); 9 | 10 | it("should initialize a simple ContentDialog control", function () { 11 | var compiledControl = initControl("
"); 12 | expect(compiledControl.winControl).toBeDefined(); 13 | expect(compiledControl.winControl instanceof WinJS.UI.ContentDialog); 14 | expect(compiledControl.className).toContain("win-contentdialog"); 15 | }); 16 | 17 | it("should use the title attribute", function () { 18 | var compiledControl = initControl("
"); 19 | expect(compiledControl.winControl.title).toEqual("ContentDialogTitle"); 20 | }); 21 | 22 | it("should use the primaryCommandText attribute", function () { 23 | var compiledControl = initControl("
"); 24 | var primaryCommand = compiledControl.querySelector(".win-contentdialog-primarycommand"); 25 | expect(primaryCommand.innerHTML).toEqual("PrimaryCommandText"); 26 | }); 27 | 28 | it("should use the primaryCommandDisabled attribute", function () { 29 | var compiledControl = initControl("
"); 30 | var primaryCommand = compiledControl.querySelector(".win-contentdialog-primarycommand"); 31 | expect(primaryCommand.disabled).toBeTruthy() 32 | }); 33 | 34 | it("should use the secondaryCommandText attribute", function () { 35 | var compiledControl = initControl("
"); 36 | var secondaryCommand = compiledControl.querySelector(".win-contentdialog-secondarycommand"); 37 | expect(secondaryCommand.innerHTML).toEqual("SecondaryCommandText"); 38 | }); 39 | 40 | it("should use the secondaryCommandDisabled attribute", function () { 41 | var compiledControl = initControl("
"); 42 | var secondaryCommand = compiledControl.querySelector(".win-contentdialog-secondarycommand"); 43 | expect(secondaryCommand.disabled).toBeTruthy(); 44 | }); 45 | 46 | it("should use the onshow event handlers and hidden attribute", function (done) { 47 | testEventChangesProperty({ 48 | control: "ContentDialog", 49 | beforeChangeEvent: "beforeshow", 50 | afterChangeEvent: "aftershow", 51 | property: "hidden", 52 | initialValue: true, 53 | finalValue: false 54 | }).then(function () { 55 | done(); 56 | }); 57 | }); 58 | 59 | it("should use the onhide event handlers and hidden attribute", function (done) { 60 | testEventChangesProperty({ 61 | control: "ContentDialog", 62 | beforeChangeEvent: "beforehide", 63 | afterChangeEvent: "afterhide", 64 | property: "hidden", 65 | initialValue: false, 66 | finalValue: true, 67 | skipInitialValueVerification: true // Need to skip initial value verification because ContentDialog has a queued aftershow event fire 68 | // after we set hidden on the control, but before the beforehide event fires, which causes a too early 69 | // update on the observable as 'hidden' 70 | }).then(function () { 71 | done(); 72 | }); 73 | }); 74 | 75 | }); -------------------------------------------------------------------------------- /tests/DatePicker.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("DatePicker control directive tests", function () { 5 | 6 | sharedSetup(); 7 | 8 | it("should initialize a simple DatePicker", function () { 9 | var compiledControl = initControl("
"); 10 | 11 | expect(compiledControl.winControl).toBeDefined(); 12 | expect(compiledControl.winControl instanceof WinJS.UI.DatePicker); 13 | expect(compiledControl.className).toContain("win-datepicker"); 14 | }); 15 | 16 | it("should use the min and max year attributes", function () { 17 | var compiledControl = initControl("
"); 18 | var winControl = compiledControl.winControl; 19 | 20 | expect(winControl.minYear).toBe(2013); 21 | expect(winControl.maxYear).toBe(2014); 22 | }); 23 | 24 | it("should use the disabled attribute", function () { 25 | var compiledControl = initControl("
"); 26 | 27 | expect(compiledControl.winControl.disabled).toBeTruthy(); 28 | }); 29 | 30 | it("should use the current attribute", function () { 31 | var ControlModel = function () { 32 | this.testDate = new Date(2013, 3, 7); 33 | }; 34 | var model = new ControlModel(); 35 | var compiledControl = initControl("
", model); 36 | var winControl = compiledControl.winControl; 37 | 38 | expect(winControl.current.getYear()).toBe(113); 39 | expect(winControl.current.getMonth()).toBe(3); 40 | expect(winControl.current.getDate()).toBe(7); 41 | }); 42 | 43 | }); -------------------------------------------------------------------------------- /tests/FlipView.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("FlipView", function () { 5 | 6 | var testDataSourceLength = 5; 7 | 8 | sharedSetup(); 9 | enableFastAnimations(); 10 | extendTimeout(); 11 | 12 | it("should initialize a simple FlipView", function () { 13 | var compiledControl = initControl("
"); 14 | 15 | expect(compiledControl.winControl).toBeDefined(); 16 | expect(compiledControl.winControl instanceof WinJS.UI.FlipView); 17 | expect(compiledControl.className).toContain("win-flipview"); 18 | }); 19 | 20 | it("should use an itemDataSource and render content with a template", function (done) { 21 | var ControlModel = function () { 22 | this.testDataSource = generateTestKOArray(testDataSourceLength); 23 | this.pageCompleted = function () { 24 | var currentPage = compiledControl.winControl._pageManager._currentPage.element; 25 | expect(currentPage.textContent).toContain("RenderedItem0"); 26 | done(); 27 | }; 28 | }; 29 | var model = new ControlModel(); 30 | var compiledControl = initControl("
" + 31 | "
" + 32 | "", model); 36 | }); 37 | 38 | it("should use an itemDataSource and render content with a WinJS template", function (done) { 39 | var ControlModel = function () { 40 | this.testDataSource = generateTestKOArray(testDataSourceLength); 41 | this.pageCompleted = function () { 42 | var currentPage = compiledControl.winControl._pageManager._currentPage.element; 43 | expect(currentPage.textContent).toContain("RenderedItem0"); 44 | done(); 45 | }; 46 | this.renderer = function (itemPromise) { 47 | return itemPromise.then(function (item) { 48 | var div = document.createElement("div"); 49 | div.className = "template"; 50 | div.textContent = "Rendered" + item.data.title; 51 | return div; 52 | }); 53 | }; 54 | }; 55 | var model = new ControlModel(); 56 | var compiledControl = initControl("
" + 57 | "
", model); 58 | }); 59 | 60 | it("should receive a page completed event", function (done) { 61 | var ControlModel = function () { 62 | this.testDataSource = generateTestKOArray(testDataSourceLength); 63 | this.pageCompleted = function () { 64 | done(); 65 | }; 66 | }; 67 | var model = new ControlModel(); 68 | var compiledControl = initControl("
" + 69 | "
", model); 70 | }); 71 | 72 | it("should receive an on page selected event", function (done) { 73 | var ControlModel = function () { 74 | this.testDataSource = generateTestKOArray(testDataSourceLength); 75 | this.pageSelected = function () { 76 | done(); 77 | }; 78 | }; 79 | var model = new ControlModel(); 80 | var compiledControl = initControl("
" + 81 | "
", model); 82 | }); 83 | 84 | it("should receive an on page visibility changed event", function (done) { 85 | var ControlModel = function () { 86 | this.testDataSource = generateTestKOArray(testDataSourceLength); 87 | this.pageVisibilityChanged = function () { 88 | done(); 89 | } 90 | }; 91 | var model = new ControlModel(); 92 | var compiledControl = initControl("
" + 93 | "
", model); 94 | }); 95 | 96 | it("should receive a datasource count changed event", function (done) { 97 | var ControlModel = function () { 98 | this.testDataSource = generateTestKOArray(testDataSourceLength); 99 | this.dataSourceCountChanged = function () { 100 | done(); 101 | } 102 | this.pageCompletedEventHandler = function () { 103 | this.testDataSource.push({ title: "Item" + (testDataSourceLength + 1) }); 104 | } 105 | }; 106 | var model = new ControlModel(); 107 | var compiledControl = initControl("
" + 108 | "
", model); 109 | 110 | }); 111 | 112 | it("should update currentPage on navigation", function (done) { 113 | var testDataSource = []; 114 | 115 | for (var i = 0; i < testDataSourceLength; i++) { 116 | testDataSource.push({ title: "Item" + i }); 117 | } 118 | 119 | testEventChangesProperty({ 120 | control: "FlipView", 121 | afterChangeEvent: "pageselected", 122 | property: "currentPage", 123 | initialValue: 1, 124 | finalValue: 2, 125 | controlLoadingComplete: function (control) { 126 | return new WinJS.Promise(function (complete) { 127 | listenOnce(control, "pagecompleted", function () { 128 | complete(); 129 | }); 130 | control.winControl.itemDataSource = (new WinJS.Binding.List(testDataSource)).dataSource; 131 | }); 132 | } 133 | }).then(function () { 134 | done(); 135 | }); 136 | }); 137 | 138 | }); -------------------------------------------------------------------------------- /tests/Flyout.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("Flyout control directive tests", function () { 5 | 6 | sharedSetup(); 7 | enableFastAnimations(); 8 | extendTimeout(); 9 | 10 | it("should initialize a simple Flyout control", function () { 11 | var compiledControl = initControl("
"); 12 | 13 | expect(compiledControl.winControl).toBeDefined(); 14 | expect(compiledControl.winControl instanceof WinJS.UI.Flyout); 15 | expect(compiledControl.className).toContain("win-flyout"); 16 | }); 17 | 18 | it("should use the alignment attribute", function () { 19 | var compiledControl = initControl("
'>
"); 20 | 21 | expect(compiledControl.winControl.alignment).toEqual("left"); 22 | }); 23 | 24 | it("should use the disabled attribute", function () { 25 | var compiledControl = initControl("
'>
"); 26 | 27 | expect(compiledControl.winControl.disabled).toBeTruthy(); 28 | }); 29 | 30 | it("should use the placement attribute", function () { 31 | var compiledControl = initControl("
'>
"); 32 | 33 | expect(compiledControl.winControl.placement).toEqual("right"); 34 | }); 35 | 36 | it("should use the anchor attribute", function () { 37 | var anchorEl = document.createElement("div"); 38 | var topNode = document.getElementById("mainNode"); 39 | 40 | topNode.appendChild(anchorEl); 41 | var ControlModel = function () { 42 | this.flyoutAnchor = anchorEl; 43 | }; 44 | var model = new ControlModel(); 45 | var compiledControl = initControl("
'>
", model); 46 | expect(compiledControl.winControl.anchor).toBe(anchorEl); 47 | }); 48 | 49 | it("should use the onshow event handlers and hidden attribute", function (done) { 50 | testEventChangesProperty({ 51 | control: "Flyout", 52 | beforeChangeEvent: "beforeshow", 53 | afterChangeEvent: "aftershow", 54 | property: "hidden", 55 | initialValue: true, 56 | finalValue: false, 57 | additionalControlProperties: "anchor: document.body" 58 | }).then(function () { 59 | done(); 60 | }); 61 | }); 62 | 63 | it("should use the onhide event handlers and hidden attribute", function (done) { 64 | testEventChangesProperty({ 65 | control: "Flyout", 66 | beforeChangeEvent: "beforehide", 67 | afterChangeEvent: "afterhide", 68 | property: "hidden", 69 | initialValue: true, // We need to initially have the Flyout hidden due to WinJS issue #1333 and then 70 | // shown in controlLoadingComplete 71 | finalValue: true, 72 | controlLoadingComplete: function (control) { 73 | return new WinJS.Promise(function (complete) { 74 | listenOnce(control, "aftershow", function () { 75 | complete(); 76 | }) 77 | control.winControl.show(); 78 | }); 79 | }, 80 | skipInitialValueVerification: true, 81 | additionalControlProperties: "anchor: document.body" 82 | }).then(function () { 83 | done(); 84 | }); 85 | }); 86 | 87 | }); -------------------------------------------------------------------------------- /tests/Hub.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("Hub control directive tests", function () { 5 | 6 | sharedSetup(); 7 | 8 | it("should initialize a simple Hub", function () { 9 | var compiledControl = initControl("
"); 10 | 11 | expect(compiledControl.winControl).toBeDefined(); 12 | expect(compiledControl.winControl instanceof WinJS.UI.Hub); 13 | expect(compiledControl.className).toContain("win-hub"); 14 | }); 15 | 16 | it("should use the orientation attribute", function () { 17 | var compiledControl = initControl("
"); 18 | 19 | expect(compiledControl.winControl.orientation).toEqual("vertical"); 20 | }); 21 | }); -------------------------------------------------------------------------------- /tests/HubSection.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("HubSection control directive tests", function () { 5 | 6 | var testDataSourceLength = 5; 7 | 8 | sharedSetup(); 9 | 10 | it("should initialize a simple Hub with a single HubSection", function () { 11 | var compiledControl = initControl("
" + 12 | "
" + 13 | "
"); 14 | 15 | expect(compiledControl.winControl).toBeDefined(); 16 | expect(compiledControl.winControl instanceof WinJS.UI.Hub); 17 | expect(compiledControl.className).toContain("win-hub"); 18 | expect(compiledControl.querySelectorAll(".win-hub-section").length).toEqual(1); 19 | }); 20 | 21 | it("should use the header attribute", function (done) { 22 | var compiledControl = initControl("
" + 23 | "
" + 24 | "
"); 25 | waitForComplete(compiledControl.winControl).then(function () { 26 | var headers = compiledControl.querySelectorAll(".win-hub-section-header"); 27 | 28 | expect(headers.length).toEqual(1); 29 | var headerContent = headers[0].querySelectorAll(".win-hub-section-header-content"); 30 | expect(headerContent.length).toEqual(1); 31 | expect(headerContent[0].innerHTML).toEqual("SimpleHeader"); 32 | done(); 33 | }); 34 | 35 | }); 36 | 37 | it("should use the isHeaderStatic attribute", function () { 38 | var compiledControl = initControl("
" + 39 | "
" + 40 | "
"); 41 | var headers = compiledControl.querySelectorAll(".win-hub-section"); 42 | 43 | expect(headers.length).toEqual(1); 44 | expect(headers[0].winControl.isHeaderStatic).toBeTruthy(); 45 | }); 46 | 47 | // TODO: Add testing for updating the foreach binding after "#4 Foreach binding with hubs is not updated after the initial binding is fixed" 48 | it("should allow foreach to be used in conjunction with the Hub to create HubSections", function (done) { 49 | var ControlModel = function () { 50 | this.testDataSource = generateTestKOArray(testDataSourceLength); 51 | }; 52 | var model = new ControlModel(); 53 | var compiledControl = initControl("
" + 54 | "" + 55 | "
" + 56 | "" + 57 | "
", model); 58 | waitForComplete(compiledControl.winControl).then(function () { 59 | var headers = compiledControl.querySelectorAll(".win-hub-section-header"); 60 | var headerContent = compiledControl.querySelectorAll(".win-hub-section-header-content"); 61 | expect(headers.length).toEqual(testDataSourceLength); 62 | expect(headerContent.length).toEqual(testDataSourceLength); 63 | for (var i = 0; i < testDataSourceLength; i++) { 64 | expect(headerContent[i].innerHTML).toEqual("Item" + i); 65 | } 66 | done(); 67 | }); 68 | 69 | }); 70 | 71 | }); -------------------------------------------------------------------------------- /tests/ItemContainer.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("ItemContainer control directive tests", function () { 5 | 6 | sharedSetup(); 7 | 8 | it("should initialize a simple ItemContainer", function () { 9 | var compiledControl = initControl("
"); 10 | 11 | expect(compiledControl.winControl).toBeDefined(); 12 | expect(compiledControl.winControl instanceof WinJS.UI.ItemContainer); 13 | expect(compiledControl.className).toContain("win-itemcontainer"); 14 | }); 15 | 16 | it("should use the draggable attribute", function () { 17 | var compiledControl = initControl("
"); 18 | var winControl = compiledControl.winControl; 19 | 20 | expect(winControl.draggable).toBeTruthy(); 21 | }); 22 | 23 | it("should use the selected attribute", function () { 24 | var compiledControl = initControl("
"); 25 | var winControl = compiledControl.winControl; 26 | 27 | expect(winControl.selected).toBeTruthy(); 28 | }); 29 | 30 | it("should use the selectionDisabled attribute", function () { 31 | var compiledControl = initControl("
"); 32 | var winControl = compiledControl.winControl; 33 | 34 | expect(winControl.selectionDisabled).toBeTruthy(); 35 | }); 36 | 37 | it("should use the tapBehavior attribute", function () { 38 | var compiledControl = initControl("
"); 39 | var winControl = compiledControl.winControl; 40 | 41 | expect(winControl.tapBehavior).toBe(WinJS.UI.TapBehavior.toggleSelect); 42 | }); 43 | 44 | it("should receive selection events", function (done) { 45 | testEventChangesProperty({ 46 | control: "ItemContainer", 47 | beforeChangeEvent: "selectionchanging", 48 | afterChangeEvent: "selectionchanged", 49 | property: "selected", 50 | initialValue: false, 51 | finalValue: true 52 | }).then(function () { 53 | done(); 54 | }); 55 | }); 56 | }); -------------------------------------------------------------------------------- /tests/ListView.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("ListView control directive tests", function () { 5 | var topNode, 6 | testDataSourceLength = 5; 7 | 8 | sharedSetup(); 9 | enableFastAnimations(); 10 | extendTimeout(); 11 | 12 | it("should initialize a simple ListView", function () { 13 | var compiledControl = initControl("
"); 14 | expect(compiledControl.winControl).toBeDefined(); 15 | expect(compiledControl.winControl instanceof WinJS.UI.ListView); 16 | expect(compiledControl.className).toContain("win-listview"); 17 | }); 18 | 19 | it("should use the itemDataSource attribute", function (done) { 20 | var ControlModel = function () { 21 | this.testDataSource = generateTestKOArray(testDataSourceLength); 22 | }; 23 | var model = new ControlModel(); 24 | var compiledControl = initControl("
", model); 25 | waitForComplete(compiledControl.winControl).then(function () { 26 | expect(compiledControl.querySelectorAll(".win-container").length).toEqual(testDataSourceLength); 27 | done(); 28 | }); 29 | }); 30 | 31 | it("should use the item template", function (done) { 32 | var ControlModel = function () { 33 | this.testDataSource = generateTestKOArray(testDataSourceLength); 34 | }; 35 | var model = new ControlModel(); 36 | var compiledControl = initControl("
" + 37 | "
" + 38 | "", model); 41 | waitForComplete(compiledControl.winControl).then(function () { 42 | var renderedItems = compiledControl.querySelectorAll(".win-item"); 43 | expect(renderedItems.length).toEqual(testDataSourceLength); 44 | for (var i = 0; i < renderedItems.length; i++) { 45 | expect(renderedItems[i].firstElementChild.innerHTML).toEqual(model.testDataSource()[i].title); 46 | } 47 | done(); 48 | }); 49 | }); 50 | 51 | it("should use the group header template", function (done) { 52 | var compiledControl; 53 | 54 | function simpleGroupingFunction(data) { 55 | return data.header; 56 | } 57 | var ControlModel = function () { 58 | this.testDataSource = ko.observableArray(); 59 | for (var i = 0; i < testDataSourceLength; i++) { 60 | this.testDataSource.push({ title: "Item" + i, header: "Header" + i }); 61 | } 62 | this.groupedDataSource = new WinJS.Binding.List(this.testDataSource()).createGrouped(simpleGroupingFunction, simpleGroupingFunction); 63 | }; 64 | var model = new ControlModel(); 65 | compiledControl = initControl("
" + 68 | "
" + 69 | "" + 72 | "", 75 | model); 76 | waitForComplete(compiledControl.winControl).then(function () { 77 | var renderedItems = compiledControl.querySelectorAll(".win-item"); 78 | expect(renderedItems.length).toEqual(testDataSourceLength); 79 | for (var i = 0; i < renderedItems.length; i++) { 80 | expect(renderedItems[i].firstElementChild.innerHTML).toEqual(model.testDataSource()[i].title); 81 | } 82 | var renderedHeaders = compiledControl.querySelectorAll(".win-groupheader"); 83 | expect(renderedHeaders.length).toEqual(testDataSourceLength); 84 | for (var i = 0; i < renderedHeaders.length; i++) { 85 | expect(renderedHeaders[i].firstElementChild.innerHTML).toEqual(model.testDataSource()[i].header); 86 | } 87 | done(); 88 | }); 89 | }); 90 | 91 | it("should use the itemsReorderable attribute", function (done) { 92 | var ControlModel = function () { 93 | this.testDataSource = generateTestKOArray(testDataSourceLength); 94 | }; 95 | var model = new ControlModel(); 96 | var compiledControl = initControl("
", 97 | model); 98 | waitForComplete(compiledControl.winControl).then(function () { 99 | expect(compiledControl.winControl.itemsReorderable).toBeTruthy(); 100 | var itemBoxes = compiledControl.querySelectorAll(".win-itembox"); 101 | for (var i = 0; i < itemBoxes.length; i++) { 102 | expect(itemBoxes[i].draggable).toBeTruthy(); 103 | } 104 | done(); 105 | }); 106 | }); 107 | 108 | it("should use the itemsDraggable attribute", function (done) { 109 | var ControlModel = function () { 110 | this.testDataSource = generateTestKOArray(testDataSourceLength); 111 | }; 112 | var model = new ControlModel(); 113 | var compiledControl = initControl("
", model); 114 | waitForComplete(compiledControl.winControl).then(function () { 115 | expect(compiledControl.winControl.itemsDraggable).toBeTruthy(); 116 | var itemBoxes = compiledControl.querySelectorAll(".win-itembox"); 117 | for (var i = 0; i < itemBoxes.length; i++) { 118 | expect(itemBoxes[i].draggable).toBeTruthy(); 119 | } 120 | done(); 121 | }); 122 | }); 123 | 124 | it("should use the loadingStateChanged event handler", function (done) { 125 | var ControlModel = function () { 126 | this.testDataSource = generateTestKOArray(testDataSourceLength); 127 | this.loadingStateChangedHandler = function () { 128 | if (compiledControl.winControl.loadingState === "complete") { 129 | done(); 130 | } 131 | }; 132 | }; 133 | var model = new ControlModel(); 134 | var compiledControl = initControl("
" + 136 | "", model); 139 | }); 140 | 141 | it("should use the maxDeferredItemCleanup attribute", function () { 142 | var compiledControl = initControl("
"); 143 | 144 | expect(compiledControl.winControl.maxDeferredItemCleanup).toEqual(10); 145 | }); 146 | 147 | it("should use the maxLeadingPages attribute", function () { 148 | var compiledControl = initControl("
"); 149 | expect(compiledControl.winControl.maxLeadingPages).toEqual(7); 150 | }); 151 | 152 | it("should use the maxTrailingPages attribute", function () { 153 | var compiledControl = initControl("
"); 154 | expect(compiledControl.winControl.maxTrailingPages).toEqual(7); 155 | }); 156 | 157 | it("should use the currentItem attribute", function () { 158 | var ControlModel = function () { 159 | this.testCurrentItem = { 160 | index: 2 161 | }; 162 | }; 163 | var model = new ControlModel(); 164 | var compiledControl = initControl("
", model); 165 | 166 | expect(compiledControl.winControl.currentItem.index).toEqual(2); 167 | }); 168 | 169 | it("should use the tapBehavior attribute", function () { 170 | var compiledControl = initControl("
"); 171 | 172 | expect(compiledControl.winControl.tapBehavior).toEqual("toggleSelect"); 173 | }); 174 | 175 | it("should use the groupHeaderTapBehavior attribute", function () { 176 | var compiledControl = initControl("
"); 177 | 178 | expect(compiledControl.winControl.groupHeaderTapBehavior).toEqual("none"); 179 | }); 180 | 181 | it("should use the selectionMode attribute", function () { 182 | var compiledControl = initControl("
"); 183 | 184 | expect(compiledControl.winControl.selectionMode).toEqual("none"); 185 | }); 186 | 187 | it("should use the layout attribute", function (done) { 188 | var ControlModel = function () { 189 | this.testDataSource = generateTestKOArray(testDataSourceLength); 190 | this.layout = new WinJS.UI.ListLayout(); 191 | }; 192 | var model = new ControlModel(); 193 | var compiledControl = initControl("
", 194 | model); 195 | waitForComplete(compiledControl.winControl).then(function () { 196 | expect(compiledControl.winControl.layout instanceof WinJS.UI.ListLayout).toBeTruthy(); 197 | done(); 198 | }); 199 | }); 200 | 201 | it("should use the onAccessibilityAnnotationComplete event", function (done) { 202 | var ControlModel = function () { 203 | this.testDataSource = generateTestKOArray(testDataSourceLength); 204 | this.handler = function (e) { 205 | done(); 206 | }; 207 | }; 208 | var model = new ControlModel(); 209 | var compiledControl = initControl("
" + 210 | "
" + 211 | "", model); 214 | }); 215 | 216 | it("should use the onContentAnimating event", function (done) { 217 | var ControlModel = function () { 218 | this.testDataSource = generateTestKOArray(testDataSourceLength); 219 | this.contentAnimatingEventHandler = function (e) { 220 | expect(e.detail.type).toEqual("entrance"); 221 | done(); 222 | }; 223 | }; 224 | var model = new ControlModel(); 225 | var compiledControl = initControl("
" + 226 | "
" + 227 | "", model); 230 | }); 231 | 232 | it("should use the selection attribute", function (done) { 233 | var ControlModel = function () { 234 | this.testDataSource = generateTestKOArray(testDataSourceLength); 235 | this.selection = ko.observableArray(); 236 | } 237 | var model = new ControlModel(); 238 | var compiledControl = initControl("
" + 239 | "
" + 240 | "", model); 243 | var control = compiledControl.winControl; 244 | waitForComplete(control).then(function () { 245 | expect(control.selection.count()).toEqual(0); 246 | model.selection.push(2); 247 | expect(control.selection.count()).toEqual(1); 248 | expect(control.selection.getIndices()[0]).toEqual(2); 249 | done(); 250 | }); 251 | }); 252 | 253 | it("should change selection from ListView", function (done) { 254 | var ControlModel = function () { 255 | this.testDataSource = generateTestKOArray(testDataSourceLength); 256 | this.selection = ko.observableArray(); 257 | } 258 | var model = new ControlModel(); 259 | var compiledControl = initControl("
" + 261 | "", model); 264 | var control = compiledControl.winControl; 265 | waitForComplete(control).then(function () { 266 | expect(control.selection.count()).toEqual(0); 267 | listenOnce(compiledControl, "selectionchanged", function () { 268 | expect(model.selection().length).toEqual(1); 269 | expect(model.selection()[0]).toEqual(2); 270 | done(); 271 | }) 272 | control.selection.set([2]); 273 | }); 274 | }); 275 | 276 | it("should apply reorders back to the scope", function (done) { 277 | var originalDataSource = []; 278 | 279 | var ControlModel = function () { 280 | var loadingStateCompletedCount = 0; 281 | this.testDataSource = generateTestKOArray(testDataSourceLength); 282 | for (var i = 0; i < testDataSourceLength; i++) { 283 | originalDataSource[i] = this.testDataSource[i]; 284 | } 285 | }; 286 | var model = new ControlModel(); 287 | var compiledControl = initControl("
" + 288 | "
" + 289 | "", model); 292 | var control = compiledControl.winControl; 293 | waitForComplete(control).then(function () { 294 | control.selection.set([testDataSourceLength - 1]); 295 | control._currentMode()._reorderItems(0, control.selection, false, true, false); 296 | return waitForComplete(control); 297 | }).then(function () { 298 | expect(originalDataSource[testDataSourceLength - 1]).toEqual(model.testDataSource[0]); 299 | expect(originalDataSource[0]).toEqual(model.testDataSource[1]); 300 | done(); 301 | }); 302 | }); 303 | 304 | it("should use the header and footer attributes", function (done) { 305 | var ControlModel = function () { 306 | this.testDataSource = generateTestKOArray(testDataSourceLength); 307 | this.headerElement = document.createElement("div"); 308 | this.footerElement = document.createElement("div"); 309 | }; 310 | var model = new ControlModel(); 311 | var compiledControl = initControl("
" + 313 | "
" + 314 | "", model); 317 | var control = compiledControl.winControl; 318 | waitForComplete(control).then(function () { 319 | expect(control.header).toEqual(model.headerElement); 320 | expect(control.footer).toEqual(model.footerElement); 321 | done(); 322 | }); 323 | }); 324 | 325 | }); -------------------------------------------------------------------------------- /tests/ListViewLayouts.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("ListView Layout control directive tests", function () { 5 | var topNode, 6 | testDataSourceLength = 5; 7 | 8 | sharedSetup(); 9 | enableFastAnimations(); 10 | 11 | it("should initialize the ListLayout", function (done) { 12 | var compiledControl; 13 | var ControlModel = function () { 14 | this.testDataSource = generateTestKOArray(testDataSourceLength); 15 | }; 16 | var model = new ControlModel(); 17 | 18 | compiledControl = initControl("
" + 20 | "
", 21 | model); 22 | waitForComplete(compiledControl.winControl).then(function () { 23 | expect(compiledControl.winControl.layout instanceof WinJS.UI.ListLayout).toBeTruthy(); 24 | done(); 25 | }); 26 | 27 | }); 28 | 29 | it("should use the orientation attribute on the ListLayout element", function (done) { 30 | var compiledControl; 31 | var ControlModel = function () { 32 | this.testDataSource = generateTestKOArray(testDataSourceLength); 33 | }; 34 | var model = new ControlModel(); 35 | 36 | compiledControl = initControl("
" + 38 | "
", 39 | model); 40 | waitForComplete(compiledControl.winControl).then(function () { 41 | expect(compiledControl.winControl.layout.orientation).toEqual("horizontal"); 42 | done(); 43 | }); 44 | }); 45 | 46 | it("should use the groupHeaderPosition attribute on the ListLayout element", function (done) { 47 | var compiledControl; 48 | var ControlModel = function () { 49 | this.testDataSource = generateTestKOArray(testDataSourceLength); 50 | }; 51 | var model = new ControlModel(); 52 | 53 | compiledControl = initControl("
" + 55 | "
" + 56 | "", 59 | model); 60 | waitForComplete(compiledControl.winControl).then(function () { 61 | expect(compiledControl.winControl.layout.groupHeaderPosition).toEqual("left"); 62 | done(); 63 | }); 64 | }); 65 | 66 | it("should initialize GridLayout", function (done) { 67 | var compiledControl; 68 | var ControlModel = function () { 69 | this.testDataSource = generateTestKOArray(testDataSourceLength); 70 | }; 71 | var model = new ControlModel(); 72 | 73 | compiledControl = initControl("
" + 75 | "
" + 76 | "", 79 | model); 80 | waitForComplete(compiledControl.winControl).then(function () { 81 | expect(compiledControl.winControl.layout instanceof WinJS.UI.GridLayout).toBeTruthy(); 82 | done(); 83 | }); 84 | }); 85 | 86 | it("should use the orientation attribute on the GridLayout element", function (done) { 87 | var compiledControl; 88 | 89 | var ControlModel = function () { 90 | this.testDataSource = generateTestKOArray(testDataSourceLength); 91 | }; 92 | var model = new ControlModel(); 93 | 94 | compiledControl = initControl("
" + 96 | "
" + 97 | "", 100 | model); 101 | waitForComplete(compiledControl.winControl).then(function () { 102 | expect(compiledControl.winControl.layout.orientation).toEqual("horizontal"); 103 | done(); 104 | }); 105 | 106 | }); 107 | 108 | it("should switch layout declared as type", function (done) { 109 | var compiledControl; 110 | var horizontalLayout = new WinJS.UI.GridLayout(); 111 | horizontalLayout.orientation = "horizontal"; 112 | var verticalLayout = new WinJS.UI.GridLayout(); 113 | verticalLayout.orientation = "vertical"; 114 | var ControlModel = function () { 115 | this.testDataSource = generateTestKOArray(testDataSourceLength); 116 | this.listViewLayout = ko.observable(horizontalLayout); 117 | }; 118 | var model = new ControlModel(); 119 | 120 | compiledControl = initControl("
" + 122 | "
" + 123 | "", 126 | model); 127 | waitForComplete(compiledControl.winControl).then(function () { 128 | expect(compiledControl.winControl.layout.orientation).toEqual("horizontal"); 129 | model.listViewLayout(verticalLayout); 130 | return waitForComplete(compiledControl.winControl); 131 | }).then(function () { 132 | expect(compiledControl.winControl.layout.orientation).toEqual("vertical"); 133 | done(); 134 | }); 135 | }); 136 | 137 | it("should switch layout declared as object", function (done) { 138 | var firstLoadingCompleteFired = false; 139 | var compiledControl; 140 | var horizontalLayout = { type: WinJS.UI.GridLayout, orientation: "horizontal" }; 141 | var verticalLayout = { type: WinJS.UI.GridLayout, orientation: "vertical" }; 142 | var ControlModel = function () { 143 | this.testDataSource = generateTestKOArray(testDataSourceLength); 144 | this.listViewLayout = ko.observable(horizontalLayout); 145 | }; 146 | var model = new ControlModel(); 147 | 148 | compiledControl = initControl("
" + 150 | "
" + 151 | "", 154 | model); 155 | waitForComplete(compiledControl.winControl).then(function () { 156 | expect(compiledControl.winControl.layout.orientation).toEqual("horizontal"); 157 | model.listViewLayout(verticalLayout); 158 | return waitForComplete(compiledControl.winControl); 159 | }).then(function () { 160 | expect(compiledControl.winControl.layout.orientation).toEqual("vertical"); 161 | done(); 162 | }); 163 | }); 164 | 165 | it("should use the groupHeaderPosition attribute on the GridLayout element", function (done) { 166 | var ControlModel = function () { 167 | this.testDataSource = generateTestKOArray(testDataSourceLength); 168 | }; 169 | var model = new ControlModel(); 170 | var compiledControl = initControl("
" + 172 | "", 175 | model); 176 | waitForComplete(compiledControl.winControl).then(function () { 177 | expect(compiledControl.winControl.layout.groupHeaderPosition).toEqual("left"); 178 | done(); 179 | }); 180 | }); 181 | 182 | it("should use the maximumRowsOrColumns attribute on the GridLayout element", function (done) { 183 | var ControlModel = function () { 184 | this.testDataSource = generateTestKOArray(testDataSourceLength); 185 | }; 186 | var model = new ControlModel(); 187 | var compiledControl = initControl("
" + 189 | "
" + 190 | "", 193 | model); 194 | waitForComplete(compiledControl.winControl).then(function () { 195 | expect(compiledControl.winControl.layout.maximumRowsOrColumns).toEqual(3); 196 | done(); 197 | }); 198 | }); 199 | }); -------------------------------------------------------------------------------- /tests/Menu.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("Menu control directive tests", function () { 5 | var topNode, 6 | testDataSourceLength = 5; 7 | 8 | sharedSetup(); 9 | enableFastAnimations(); 10 | extendTimeout(); 11 | 12 | it("should initialize a simple Menu", function () { 13 | var compiledControl = initControl("
"); 14 | 15 | expect(compiledControl.winControl).toBeDefined(); 16 | expect(compiledControl.winControl instanceof WinJS.UI.Menu); 17 | expect(compiledControl.className).toContain("win-menu"); 18 | }); 19 | 20 | it("should use child MenuCommands", function () { 21 | var compiledControl = initControl("
" + 22 | "" + 23 | "" + 24 | "
"); 25 | 26 | expect(compiledControl.winControl).toBeDefined(); 27 | expect(compiledControl.winControl instanceof WinJS.UI.Menu); 28 | expect(compiledControl.className).toContain("win-menu"); 29 | expect(compiledControl.querySelectorAll(".win-command").length).toEqual(2); 30 | }); 31 | 32 | it("should use the alignment attribute", function () { 33 | var compiledControl = initControl("
"); 34 | 35 | expect(compiledControl.winControl.alignment).toEqual("right"); 36 | }); 37 | 38 | it("should use the disabled attribute", function () { 39 | var compiledControl = initControl("
"); 40 | 41 | expect(compiledControl.winControl.disabled).toBeTruthy(); 42 | }); 43 | 44 | it("should use the placement attribute", function () { 45 | var compiledControl = initControl("
"); 46 | 47 | expect(compiledControl.winControl.placement).toEqual("top"); 48 | }); 49 | 50 | it("should use the onshow event handlers and hidden attribute", function (done) { 51 | testEventChangesProperty({ 52 | control: "Menu", 53 | beforeChangeEvent: "beforeshow", 54 | afterChangeEvent: "aftershow", 55 | property: "hidden", 56 | initialValue: true, 57 | finalValue: false, 58 | additionalControlProperties: "anchor: document.body" 59 | }).then(function () { 60 | done(); 61 | }); 62 | }); 63 | 64 | it("should use the onhide event handlers and hidden attribute", function (done) { 65 | testEventChangesProperty({ 66 | control: "Menu", 67 | beforeChangeEvent: "beforehide", 68 | afterChangeEvent: "afterhide", 69 | property: "hidden", 70 | initialValue: true, // We need to initially have the Menu hidden due to WinJS issue #1333 and then 71 | // shown in controlLoadingComplete 72 | finalValue: true, 73 | controlLoadingComplete: function (control) { 74 | return new WinJS.Promise(function (complete) { 75 | listenOnce(control, "aftershow", function () { 76 | complete(); 77 | }) 78 | control.winControl.show(); 79 | }); 80 | }, 81 | skipInitialValueVerification: true, 82 | additionalControlProperties: "anchor: document.body" 83 | }).then(function () { 84 | done(); 85 | }); 86 | }); 87 | 88 | }); -------------------------------------------------------------------------------- /tests/MenuCommand.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("MenuCommand control directive tests", function () { 5 | 6 | sharedSetup(); 7 | 8 | it("should use initialize a Menu containing two child MenuCommands", function () { 9 | var compiledControl = initControl("
" + 10 | "" + 11 | "" + 12 | "
"); 13 | var winControl = compiledControl.winControl; 14 | 15 | expect(compiledControl.winControl).toBeDefined(); 16 | expect(compiledControl.winControl instanceof WinJS.UI.Menu); 17 | expect(compiledControl.className).toContain("win-menu"); 18 | expect(compiledControl.querySelectorAll(".win-command").length).toEqual(2); 19 | }); 20 | 21 | it("should use the id attribute on MenuCommands", function () { 22 | var compiledControl = initControl("
" + 23 | "" + 24 | "" + 25 | "
"); 26 | var commands = compiledControl.querySelectorAll(".win-command"); 27 | 28 | expect(commands[0].id).toEqual("command1"); 29 | expect(commands[1].id).toEqual("command2"); 30 | }); 31 | 32 | it("should use the label attribute on MenuCommands", function () { 33 | var compiledControl = initControl("
" + 34 | "" + 35 | "" + 36 | "
"); 37 | var commands = compiledControl.querySelectorAll(".win-command"); 38 | 39 | expect(commands[0].querySelector(".win-label").innerHTML).toEqual("command1"); 40 | expect(commands[1].querySelector(".win-label").innerHTML).toEqual("command2"); 41 | }); 42 | 43 | it("should use the disabled attribute on MenuCommands", function () { 44 | var compiledControl = initControl("
" + 45 | "" + 46 | "" + 47 | "
"); 48 | var commands = compiledControl.querySelectorAll(".win-command"); 49 | 50 | expect(commands[0].winControl.disabled).toBeTruthy(); 51 | expect(commands[1].winControl.disabled).toBeFalsy(); 52 | }); 53 | 54 | it("should use the extraClass attribute on MenuCommands", function () { 55 | var compiledControl = initControl("
" + 56 | "" + 57 | "
"); 58 | var commands = compiledControl.querySelectorAll(".win-command"); 59 | 60 | expect(commands[0].className).toContain("extraClass1"); 61 | }); 62 | 63 | it("should use the section attribute on MenuCommands", function () { 64 | var compiledControl = initControl("
" + 65 | "" + 66 | "" + 67 | "
"); 68 | var commands = compiledControl.querySelectorAll(".win-command"); 69 | 70 | expect(commands[0].winControl.section).toEqual("primary"); 71 | expect(commands[1].winControl.section).toEqual("secondary"); 72 | }); 73 | 74 | it("should use the type attribute on MenuCommands", function () { 75 | var compiledControl = initControl("
" + 76 | "" + 77 | "" + 78 | "
"); 79 | var commands = compiledControl.querySelectorAll(".win-command"); 80 | 81 | expect(commands[0].winControl.type).toEqual("button"); 82 | expect(commands[1].winControl.type).toEqual("toggle"); 83 | }); 84 | 85 | }); -------------------------------------------------------------------------------- /tests/Pivot.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("Pivot control directive tests", function () { 5 | var topNode, 6 | testDataSourceLength = 5; 7 | 8 | sharedSetup(); 9 | enableFastAnimations(); 10 | extendTimeout(); 11 | 12 | it("should initialize a simple Pivot", function () { 13 | var compiledControl = initControl("
"); 14 | expect(compiledControl.winControl).toBeDefined(); 15 | expect(compiledControl.winControl instanceof WinJS.UI.Pivot); 16 | expect(compiledControl.className).toContain("win-pivot"); 17 | }); 18 | 19 | it("should use the locked attribute", function () { 20 | var compiledControl = initControl("
"); 21 | expect(compiledControl.winControl.locked).toBeTruthy(); 22 | }); 23 | 24 | it("should use the title attribute", function () { 25 | var compiledControl = initControl("
"); 26 | expect(compiledControl.winControl.title).toEqual("PivotTitle"); 27 | }); 28 | 29 | it("should use inline pivot items", function (done) { 30 | var ControlModel = function () { 31 | // The Pivot doesn't have a loadingStateChanged event (or any similar loading complete events). 32 | // We'll use item animation end to ensure it loaded 33 | this.itemAnimationEndHandler = function (e) { 34 | var pivotHeaders = compiledControl.querySelectorAll(".win-pivot-header"); 35 | var pivotItemContent = compiledControl.querySelectorAll(".win-pivot-item-content"); 36 | expect(pivotHeaders.length).toEqual(2); 37 | expect(pivotItemContent.length).toEqual(2); 38 | expect(pivotHeaders[0].innerHTML).toEqual("Header1"); 39 | expect(pivotHeaders[1].innerHTML).toEqual("Header2"); 40 | expect(pivotItemContent[0].innerHTML).toEqual("Item1"); 41 | expect(pivotItemContent[1].innerHTML).toEqual("Item2"); 42 | done(); 43 | } 44 | }; 45 | var model = new ControlModel(); 46 | var compiledControl = initControl("
" + 47 | "
Item1
" + 48 | "
Item2
" + 49 | "
", model); 50 | }); 51 | 52 | it("should use the selectedIndex attribute", function (done) { 53 | var pivot; 54 | var selectionChangeCount = 0; 55 | var firstItemAnimationEndHandlerFired = false; 56 | var ControlModel = function () { 57 | this.selectedIndex = ko.observable(0); 58 | 59 | }; 60 | var model = new ControlModel(); 61 | var compiledControl = initControl("
" + 62 | "
Item1
" + 63 | "
Item2
" + 64 | "
", model); 65 | 66 | pivot = compiledControl.winControl; 67 | listenOnce(pivot, "itemanimationend", function () { 68 | expect(pivot.selectedIndex).toEqual(0); 69 | listenOnce(pivot, "selectionchanged", function (event) { 70 | expect(event.detail.index).toEqual(1); 71 | done(); 72 | }) 73 | model.selectedIndex(1); 74 | }) 75 | }); 76 | 77 | it("should use the selectedItem attribute", function (done) { 78 | var compiledControl; 79 | var pivot; 80 | var selectionChangeCount = 0; 81 | var ControlModel = function () { 82 | this.selectedItem = ko.observable(null); 83 | }; 84 | var model = new ControlModel(); 85 | 86 | compiledControl = initControl("
" + 87 | "
Item1
" + 88 | "
Item2
" + 89 | "
", 90 | model), 91 | pivot = compiledControl.winControl; 92 | listenOnce(pivot, "itemanimationend", function () { 93 | expect(pivot.selectedIndex).toEqual(0); 94 | listenOnce(pivot, "selectionchanged", function (event) { 95 | expect(event.detail.index).toEqual(1); 96 | done(); 97 | }) 98 | model.selectedItem(pivot.items.getAt(1)); 99 | }) 100 | }); 101 | 102 | it("should ensure changing selection updates ko selectedItem", function (done) { 103 | testEventChangesProperty({ 104 | control: "Pivot", 105 | afterChangeEvent: "selectionchanged", 106 | property: "selectedItem", 107 | initialValue: null, 108 | finalValue: function (control) { 109 | return control.winControl.items.getAt(1); 110 | }, 111 | childrenMarkup: "
Item1
" + 112 | "
Item2
", 113 | controlLoadingComplete: function (control) { 114 | return new WinJS.Promise(function (complete) { 115 | listenOnce(control, "itemanimationend", function () { 116 | complete(); 117 | }); 118 | }); 119 | } 120 | }).then(function () { 121 | done(); 122 | }); 123 | }); 124 | 125 | it("should ensure changing selection updates ko selectedIndex", function (done) { 126 | testEventChangesProperty({ 127 | control: "Pivot", 128 | afterChangeEvent: "selectionchanged", 129 | property: "selectedIndex", 130 | initialValue: 0, 131 | finalValue: 1, 132 | childrenMarkup: "
Item1
" + 133 | "
Item2
", 134 | controlLoadingComplete: function (control) { 135 | return new WinJS.Promise(function (complete) { 136 | listenOnce(control, "itemanimationend", function () { 137 | complete(); 138 | }); 139 | }); 140 | } 141 | }).then(function () { 142 | done(); 143 | }); 144 | }); 145 | 146 | it("should use the itemanimationend event handler", function (done) { 147 | var ControlModel = function () { 148 | this.itemAnimationEndHandler = function (e) { 149 | done(); 150 | }; 151 | }; 152 | var model = new ControlModel(); 153 | var compiledControl = initControl("
" + 154 | "
Item1
" + 155 | "
", 156 | model); 157 | }); 158 | 159 | it("should use the custom header attributes", function () { 160 | var compiledControl; 161 | var ControlModel = function () { 162 | this.customLeftHeader = document.createElement("div"); 163 | this.customRightHeader = document.createElement("div"); 164 | this.itemAnimationEndHandler = function (e) { 165 | var winControl = compiledControl.winControl; 166 | expect(winControl.customLeftHeader).toEqual(this.customLeftHeader); 167 | expect(winControl.customRightHeader).toEqual(this.customRightHeader); 168 | }; 169 | }; 170 | var model = new ControlModel(); 171 | 172 | compiledControl = initControl("
" + 173 | "
Item1
" + 174 | "
", 175 | model); 176 | }); 177 | 178 | it("should render child pivot items with observable array data", function (done) { 179 | var ControlModel = function () { 180 | this.childData = ko.observableArray([1, 2, 3]); 181 | } 182 | var model = new ControlModel(); 183 | var compiledControl = initControl("
" + 184 | "
" + 185 | "
", 186 | model); 187 | waitForComplete(document.getElementById("listView").winControl).then(function () { 188 | expect(compiledControl.querySelectorAll(".win-item").length > 0); 189 | done(); 190 | }); 191 | }); 192 | 193 | it("should let foreach add new pivot items", function (done) { 194 | var compiledControl; 195 | var ControlModel = function () { 196 | this.items = ko.observableArray([ 197 | { title: "Item0" }, 198 | { title: "Item1" } 199 | ]); 200 | this.itemAnimationEndHandler = function (e) { 201 | var pivot = compiledControl.winControl; 202 | var pivotHeaders = compiledControl.querySelectorAll(".win-pivot-header"); 203 | expect(pivotHeaders.length).toEqual(2); 204 | expect(pivotHeaders[0].innerHTML).toEqual("Item0"); 205 | expect(pivotHeaders[1].innerHTML).toEqual("Item1"); 206 | /* 207 | 208 | // TODO: Enable the foreach items updating test after #4 Foreach binding with hubs is not updated after the initial binding is fixed 209 | model.items.push({ title: "NewItem" }); 210 | pivotHeaders = compiledControl.querySelectorAll(".win-pivot-header"); 211 | expect(pivotHeaders.length).toEqual(3); 212 | expect(pivotHeaders[0].innerHTML).toEqual("Item0"); 213 | expect(pivotHeaders[1].innerHTML).toEqual("Item1"); 214 | expect(pivotHeaders[2].innerHTML).toEqual("NewItem"); 215 | */ 216 | done(); 217 | } 218 | } 219 | var model = new ControlModel(); 220 | 221 | compiledControl = initControl("
" + 222 | "" + 223 | "
" + 224 | "" + 225 | "
", 226 | model); 227 | }); 228 | 229 | }); -------------------------------------------------------------------------------- /tests/Rating.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("Rating control tests", function () { 5 | var testTimeout = 5000; 6 | 7 | sharedSetup(); 8 | 9 | it("should initialize a simple Rating control", function () { 10 | var simpleRatingControl = initControl("
"); 11 | 12 | expect(simpleRatingControl.winControl).toBeDefined(); 13 | expect(simpleRatingControl.winControl instanceof WinJS.UI.Rating); 14 | expect(simpleRatingControl.className).toContain("win-rating"); 15 | 16 | }); 17 | 18 | it("should use rating attributes", function () { 19 | var ratingControlWithRatingAttributes = initControl("
"); 20 | var winControl = ratingControlWithRatingAttributes.winControl; 21 | 22 | expect(winControl.maxRating).toEqual(10); 23 | expect(winControl.userRating).toEqual(9); 24 | expect(winControl.averageRating).toEqual(3); 25 | }); 26 | 27 | it("should use the disabled attribute", function () { 28 | var disabledRatingControl = initControl("
"); 29 | 30 | expect(disabledRatingControl.winControl.disabled).toBeTruthy(); 31 | }); 32 | }); -------------------------------------------------------------------------------- /tests/SemanticZoom.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("SemanticZoom control directive tests", function () { 5 | var topNode, 6 | testDataSourceLength = 5; 7 | 8 | sharedSetup(); 9 | enableFastAnimations(); 10 | 11 | it("should initialize a simple SemanticZoom", function () { 12 | var rawData = []; 13 | for (var i = 0; i < testDataSourceLength; i++) { 14 | rawData.push({ title: "Item" + i }); 15 | } 16 | var ControlModel = function () { 17 | function simpleGroupingFunction(data) { 18 | return data.title; 19 | } 20 | this.zoomedInSource = new WinJS.Binding.List(rawData).createGrouped(simpleGroupingFunction, simpleGroupingFunction); 21 | this.zoomedOutSource = this.zoomedInSource.groups; 22 | }; 23 | var model = new ControlModel(); 24 | var compiledControl = initControl("
" + 25 | "
" + 26 | "
" + 27 | "
", 28 | model); 29 | 30 | expect(compiledControl.winControl).toBeDefined(); 31 | expect(compiledControl.winControl instanceof WinJS.UI.SemanticZoom); 32 | expect(compiledControl.className).toContain("win-semanticzoom"); 33 | }); 34 | 35 | it("should use the enableButton attribute", function () { 36 | var rawData = []; 37 | for (var i = 0; i < testDataSourceLength; i++) { 38 | rawData.push({ title: "Item" + i }); 39 | } 40 | var ControlModel = function () { 41 | function simpleGroupingFunction(data) { 42 | return data.title; 43 | } 44 | this.zoomedInSource = new WinJS.Binding.List(rawData).createGrouped(simpleGroupingFunction, simpleGroupingFunction); 45 | this.zoomedOutSource = this.zoomedInSource.groups; 46 | }; 47 | var model = new ControlModel(); 48 | var compiledControl = initControl("
" + 49 | "
" + 50 | "
" + 51 | "
", 52 | model); 53 | 54 | expect(compiledControl.winControl.enableButton).toBeFalsy(); 55 | }); 56 | 57 | it("should use the zoomFactor attribute", function () { 58 | var rawData = []; 59 | for (var i = 0; i < testDataSourceLength; i++) { 60 | rawData.push({ title: "Item" + i }); 61 | } 62 | var ControlModel = function () { 63 | function simpleGroupingFunction(data) { 64 | return data.title; 65 | } 66 | this.zoomedInSource = new WinJS.Binding.List(this.rawData).createGrouped(simpleGroupingFunction, simpleGroupingFunction); 67 | this.zoomedOutSource = this.zoomedInSource.groups; 68 | }; 69 | var model = new ControlModel(); 70 | var compiledControl = initControl("
" + 71 | "
" + 72 | "
" + 73 | "
", 74 | model); 75 | 76 | expect(compiledControl.winControl.zoomFactor).toEqual(0.25); 77 | }); 78 | 79 | it("should use the onZoomChanged event handler", function (done) { 80 | var rawData = []; 81 | for (var i = 0; i < testDataSourceLength; i++) { 82 | rawData.push({ title: "Item" + i }); 83 | } 84 | function simpleGroupingFunction(data) { 85 | return data.title; 86 | } 87 | var zoomedInSource = new WinJS.Binding.List(rawData).createGrouped(simpleGroupingFunction, simpleGroupingFunction); 88 | var controlModelAdditionalProperties = { 89 | zoomedInSource: zoomedInSource, 90 | zoomedOutSource: zoomedInSource.groups 91 | }; 92 | testEventChangesProperty({ 93 | control: "SemanticZoom", 94 | afterChangeEvent: "zoomchanged", 95 | property: "zoomedOut", 96 | initialValue: false, 97 | finalValue: true, 98 | childrenMarkup: "
" + 99 | "
", 100 | additionalControlModelProperties: controlModelAdditionalProperties 101 | }).then(function () { 102 | done(); 103 | }); 104 | }); 105 | 106 | }); -------------------------------------------------------------------------------- /tests/SplitView.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("SplitView control directive tests", function () { 5 | 6 | sharedSetup(); 7 | enableFastAnimations(); 8 | 9 | it("should initialize a simple SplitView", function () { 10 | var compiledControl = initControl("
"); 11 | 12 | expect(compiledControl.winControl).toBeDefined(); 13 | expect(compiledControl.winControl instanceof WinJS.UI.SplitView); 14 | expect(compiledControl.className).toContain("win-splitview"); 15 | }); 16 | 17 | it("should use the closedDisplayMode attribute", function () { 18 | var compiledControl = initControl("
"); 19 | 20 | expect(compiledControl.winControl.closedDisplayMode).toEqual("inline"); 21 | }); 22 | 23 | it("should use the openedDisplayMode attribute", function () { 24 | var compiledControl = initControl("
"); 25 | 26 | expect(compiledControl.winControl.openedDisplayMode).toEqual("inline"); 27 | }); 28 | 29 | it("should use the panePlacement attribute", function () { 30 | var compiledControl = initControl("
"); 31 | 32 | expect(compiledControl.winControl.panePlacement).toEqual("top"); 33 | }); 34 | 35 | it("should use content and pane nodes", function () { 36 | var compiledControl = initControl("
" + 37 | "
" + 38 | "
" + 39 | "
" + 40 | "
"); 41 | 42 | var splitview = compiledControl.winControl; 43 | expect(splitview.paneElement.parentNode.querySelectorAll(".paneShouldBeInDom").length).toEqual(1); 44 | expect(splitview.contentElement.parentNode.querySelectorAll(".contentShouldBeInDom").length).toEqual(2); 45 | }); 46 | 47 | it("should use the open event handlers and paneOpened attribute", function (done) { 48 | testEventChangesProperty({ 49 | control: "SplitView", 50 | beforeChangeEvent: "beforeopen", 51 | afterChangeEvent: "afteropen", 52 | property: "paneOpened", 53 | initialValue: false, 54 | finalValue: true 55 | }).then(function () { 56 | done(); 57 | }); 58 | }); 59 | 60 | it("should use the close event handlers and paneOpened attribute", function (done) { 61 | testEventChangesProperty({ 62 | control: "SplitView", 63 | beforeChangeEvent: "beforeclose", 64 | afterChangeEvent: "afterclose", 65 | property: "paneOpened", 66 | initialValue: true, 67 | finalValue: false 68 | }).then(function () { 69 | done(); 70 | }); 71 | }); 72 | }); -------------------------------------------------------------------------------- /tests/SplitViewCommand.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("SplitViewCommand control directive tests", function () { 5 | 6 | sharedSetup(); 7 | 8 | it("should initialize a SplitViewCommand", function () { 9 | var compiledControl = initControl("
"); 10 | 11 | expect(compiledControl.winControl).toBeDefined(); 12 | expect(compiledControl.winControl instanceof WinJS.UI.SplitViewCommand); 13 | expect(compiledControl.className).toContain("win-splitviewcommand"); 14 | }); 15 | 16 | it("should use the label attribute", function () { 17 | var compiledControl = initControl("
"); 18 | 19 | expect(compiledControl.querySelector(".win-splitviewcommand-label").innerHTML).toEqual("command1"); 20 | }); 21 | 22 | it("should use the onInvoked attribute", function (done) { 23 | var gotInvokedEvent = false; 24 | var ControlModel = function () { 25 | this.onInvoked = function () { 26 | gotInvokedEvent = true; 27 | done(); 28 | }; 29 | }; 30 | var model = new ControlModel(); 31 | var compiledControl = initControl("
", model); 32 | var button = compiledControl.querySelector(".win-splitviewcommand-button"); 33 | expect(gotInvokedEvent).toBeFalsy(); 34 | button.click(); 35 | }); 36 | 37 | }); -------------------------------------------------------------------------------- /tests/SplitViewPaneToggle.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("SplitViewPaneToggle control directive tests", function () { 5 | 6 | sharedSetup(); 7 | 8 | it("should initialize a simple SplitViewPaneToggle", function () { 9 | var compiledControl = initControl(""); 10 | 11 | expect(compiledControl.winControl).toBeDefined(); 12 | expect(compiledControl.winControl instanceof WinJS.UI.SplitViewPaneToggle); 13 | expect(compiledControl.className).toContain("win-splitviewpanetoggle"); 14 | }); 15 | 16 | it("should use the splitView attribute", function () { 17 | var ControlModel = function () { 18 | this.splitView = new WinJS.UI.SplitView().element; 19 | }; 20 | var model = new ControlModel(); 21 | var control = initControl("", model); 22 | 23 | expect(control.winControl.splitView).toEqual(model.splitView); 24 | }); 25 | 26 | it("should use the onInvoked attribute", function (done) { 27 | var ControlModel = function () { 28 | this.invokedHandler = function () { 29 | done(); 30 | }; 31 | }; 32 | var model = new ControlModel(); 33 | var compiledControl = initControl("", model); 34 | 35 | compiledControl.click(); 36 | }); 37 | 38 | }); -------------------------------------------------------------------------------- /tests/TestHelper.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | "use strict"; 3 | 4 | function initControl(markup, model) { 5 | var element = document.createElement("div"); 6 | var topNode = document.getElementById("mainNode"); 7 | 8 | element.innerHTML = markup; 9 | var controlElement = element.childNodes[0]; 10 | controlElement.id = "testControl"; 11 | topNode.appendChild(element); 12 | ko.applyBindings(model, topNode); 13 | 14 | return controlElement; 15 | } 16 | 17 | function sharedSetup() { 18 | var topNode; 19 | 20 | beforeEach(function () { 21 | topNode = document.createElement("div"); 22 | topNode.id = "mainNode"; 23 | document.body.appendChild(topNode); 24 | }); 25 | 26 | afterEach(function () { 27 | ko.cleanNode(topNode); 28 | document.body.removeChild(topNode); 29 | topNode = null; 30 | }); 31 | } 32 | 33 | function enableFastAnimations() { 34 | beforeEach(function () { 35 | WinJS.Utilities._fastAnimations = true; 36 | }); 37 | 38 | afterEach(function () { 39 | WinJS.Utilities._fastAnimations = false; 40 | }); 41 | } 42 | 43 | function extendTimeout() { 44 | var originalTimeout; 45 | 46 | beforeEach(function () { 47 | originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; 48 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 15000; 49 | }); 50 | 51 | afterEach(function () { 52 | jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; 53 | }); 54 | } 55 | 56 | function generateTestKOArray(testDataSourceLength) { 57 | var arr = ko.observableArray(); 58 | 59 | for (var i = 0; i < testDataSourceLength; i++) { 60 | arr.push({ title: "Item" + i }); 61 | } 62 | 63 | return arr; 64 | } 65 | 66 | // This helper function is useful for WinJS controls such as ListView and Hub 67 | // which use the loadingstatechanged event and loadingState property to report they completed loading 68 | function waitForComplete(control) { 69 | return new WinJS.Promise(function (complete) { 70 | if (control.loadingState === "complete") { 71 | complete(); 72 | } else { 73 | var loadingStateChanged = function () { 74 | if (control.loadingState === "complete") { 75 | control.removeEventListener("loadingstatechanged", loadingStateChanged); 76 | complete(); 77 | } 78 | }; 79 | control.addEventListener("loadingstatechanged", loadingStateChanged); 80 | } 81 | }); 82 | } 83 | 84 | function listenOnce(target, type, listener) { 85 | var listenerFunc = function (e) { 86 | target.removeEventListener(type, listenerFunc); 87 | listener(e); 88 | }; 89 | target.addEventListener(type, listenerFunc); 90 | } 91 | 92 | // A test helper to test events changing WinJS control properties that are bound to knockout observables 93 | function testEventChangesProperty(testObject) { 94 | // The name of control to test 95 | var control = testObject.control, 96 | // The event that fires before change of the property (i.e. beforeopen) [optional] 97 | beforeChangeEvent = testObject.beforeChangeEvent, 98 | // The event that fires after change of the property (i.e. afteropen) 99 | afterChangeEvent = testObject.afterChangeEvent, 100 | // The property of control that is expected to change after afterchange event fires (i.e opened) 101 | property = testObject.property, 102 | // The initial value of the property that gets bound to a ko observable, can be a primitive or a function taking the control that returns the value 103 | initialValue = testObject.initialValue, 104 | // The final value of the property that is expected to be the same as the ko observable, can be a primitive or a function taking the control 105 | // that returns the value 106 | finalValue = testObject.finalValue, 107 | // The children markup of the control [optional] 108 | childrenMarkup = testObject.childrenMarkup, 109 | // Sibling markup of the control [optional] 110 | siblingMarkup = testObject.siblingMarkup, 111 | // The tag of the control [optional, defaults to div] 112 | controlElementTag = testObject.controlElementTag ? testObject.controlElementTag : "div", 113 | // A function returning a promise that waits for the completion of control loading. The final value will not be set on the property 114 | // before loading is complete [optional, defaults to empty promise] 115 | controlLoadingComplete = testObject.controlLoadingComplete ? testObject.controlLoadingComplete : function () { 116 | return WinJS.Promise.as() 117 | }, 118 | // Additional control model properties can be defined here, they will be mixed in with the properties that represent the beforechange, afterchange event handlers and the changing property [optional, defaults to empty object] 119 | additionalControlModelProperties = testObject.additionalControlModelProperties ? testObject.additionalControlModelProperties : {}, 120 | // Additional properties that will be added to markup of the tested control in its bind dataset [optional, defaults to empty string] 121 | additionalControlProperties = testObject.additionalControlProperties ? (testObject.additionalControlProperties + ", ") : "", 122 | // Flag to pass in if the test should skip the initial value to be verified 123 | // to equal the property observable in before change event, useful for tests that modify the initial property value on their 124 | // controlLoadingComplete promise [optional, defaults to false] 125 | skipInitialValueVerification = (testObject.skipInitialValueVerification !== undefined) ? testObject.skipInitialValueVerification : false; 126 | 127 | return new WinJS.Promise(function (complete) { 128 | var gotBeforeChangeEvent = false; 129 | var compiledControl; 130 | var setupComplete = false; 131 | var ControlModel = function () { 132 | if (beforeChangeEvent) { 133 | this.beforeChangeEventHandler = function (e) { 134 | gotBeforeChangeEvent = true; 135 | if (!skipInitialValueVerification) { 136 | if (typeof initialValue === "function") { 137 | expect(this.changingProperty()).toEqual(initialValue(compiledControl)); 138 | } else { 139 | expect(this.changingProperty()).toEqual(initialValue); 140 | } 141 | } 142 | return true; 143 | }; 144 | } 145 | this.afterChangeEventHandler = function (e) { 146 | if (setupComplete) { 147 | if (beforeChangeEvent) { 148 | expect(gotBeforeChangeEvent).toBeTruthy(); 149 | } 150 | if (typeof finalValue === "function") { 151 | expect(this.changingProperty()).toEqual(finalValue(compiledControl)); 152 | } else { 153 | expect(this.changingProperty()).toEqual(finalValue); 154 | } 155 | complete(); 156 | } 157 | }; 158 | if (typeof initialValue === "function") { 159 | this.changingProperty = ko.observable(initialValue(compiledControl)); 160 | } else { 161 | this.changingProperty = ko.observable(initialValue); 162 | } 163 | }; 164 | 165 | // Create the model object from the mix of test helper ControlModel and additional properties that can come from test 166 | WinJS.Class.mix(ControlModel, additionalControlModelProperties); 167 | var model = new ControlModel(); 168 | var controlMarkup = "<" + controlElementTag + " data-bind='win" + control + ": { " + additionalControlProperties + property + ": changingProperty }, event: { "; 169 | if (beforeChangeEvent) { 170 | controlMarkup += beforeChangeEvent + ": beforeChangeEventHandler, "; 171 | } 172 | controlMarkup += afterChangeEvent + ": afterChangeEventHandler } '>"; 173 | if (childrenMarkup) { 174 | controlMarkup += childrenMarkup; 175 | } 176 | controlMarkup += ""; 177 | compiledControl = initControl(controlMarkup, model); 178 | controlLoadingComplete(compiledControl).then(function () { 179 | setupComplete = true; 180 | if (typeof finalValue === "function") { 181 | compiledControl.winControl[property] = finalValue(compiledControl); 182 | } else { 183 | compiledControl.winControl[property] = finalValue; 184 | } 185 | }); 186 | }); 187 | } 188 | -------------------------------------------------------------------------------- /tests/TimePicker.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("TimePicker control directive tests", function () { 5 | 6 | sharedSetup(); 7 | 8 | it("should initialize a simple TimePicker", function () { 9 | var compiledControl = initControl("
"); 10 | 11 | expect(compiledControl.winControl).toBeDefined(); 12 | expect(compiledControl.winControl instanceof WinJS.UI.TimePicker); 13 | expect(compiledControl.className).toContain("win-timepicker"); 14 | }); 15 | 16 | it("should use the clock attribute", function () { 17 | var ControlModel = function () { 18 | this.testClock = "24HourClock"; 19 | }; 20 | var model = new ControlModel(); 21 | var compiledControl = initControl("
", model); 22 | 23 | expect(compiledControl.winControl.clock).toBe("24HourClock"); 24 | }); 25 | 26 | it("should use the disabled attribute", function () { 27 | var compiledControl = initControl("
"); 28 | 29 | expect(compiledControl.winControl.disabled).toBeTruthy(); 30 | }); 31 | 32 | it("should use the current attribute", function () { 33 | var ControlModel = function () { 34 | this.testDate = new Date(2000, 1, 1, 11, 12); 35 | }; 36 | var model = new ControlModel(); 37 | var compiledControl = initControl("
", model); 38 | var winControl = compiledControl.winControl; 39 | 40 | expect(winControl.current.getHours()).toBe(11); 41 | expect(winControl.current.getMinutes()).toBe(12); 42 | }); 43 | 44 | it("should use the minuteIncrement attribute", function () { 45 | var compiledControl = initControl("
"); 46 | 47 | expect(compiledControl.winControl.minuteIncrement).toBe(10); 48 | }); 49 | 50 | }); -------------------------------------------------------------------------------- /tests/ToggleSwitch.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("ToggleSwitch control directive tests", function () { 5 | 6 | sharedSetup(); 7 | 8 | it("should initialize a simple toggle switch", function () { 9 | var compiledControl = initControl("
"); 10 | 11 | expect(compiledControl.winControl).toBeDefined(); 12 | expect(compiledControl.winControl instanceof WinJS.UI.ToggleSwitch); 13 | expect(compiledControl.className).toContain("win-toggleswitch"); 14 | }); 15 | 16 | it("should use the checked attribute", function () { 17 | var compiledControl = initControl("
"); 18 | var winControl = compiledControl.winControl; 19 | 20 | expect(winControl.checked).toBeTruthy(); 21 | }); 22 | 23 | it("should use label attributes", function () { 24 | var compiledControl = initControl("
"); 25 | 26 | expect(compiledControl.querySelector(".win-toggleswitch-value-on").innerHTML).toBe("onLabel"); 27 | expect(compiledControl.querySelector(".win-toggleswitch-value-off").innerHTML).toBe("offLabel"); 28 | }); 29 | 30 | it("should use the title attribute", function () { 31 | var compiledControl = initControl("
"); 32 | 33 | expect(compiledControl.querySelector(".win-toggleswitch-header").innerHTML).toBe("toggleTitle"); 34 | }); 35 | 36 | it("should use the disabled attribute", function () { 37 | var compiledControl = initControl("
"); 38 | 39 | expect(compiledControl.winControl.disabled).toBeTruthy(); 40 | }); 41 | 42 | it("should allow event handlers to be set up in markup", function (done) { 43 | var winControl; 44 | var ControlModel = function () { 45 | this.changedEventHandler = function (e) { 46 | expect(winControl.checked).toBeTruthy(); 47 | done(); 48 | }; 49 | this.isChecked = ko.observable(false); 50 | }; 51 | var model = new ControlModel() 52 | var compiledControl = initControl("
", model); 53 | 54 | winControl = compiledControl.winControl; 55 | expect(winControl.checked).toBeFalsy(); 56 | model.isChecked(true); 57 | }); 58 | 59 | }); 60 | -------------------------------------------------------------------------------- /tests/ToolBar.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("ToolBar control directive tests", function () { 5 | 6 | sharedSetup(); 7 | enableFastAnimations(); 8 | extendTimeout(); 9 | 10 | it("should initialize a simple ToolBar", function () { 11 | var compiledControl = initControl("
"); 12 | 13 | expect(compiledControl.winControl).toBeDefined(); 14 | expect(compiledControl.winControl instanceof WinJS.UI.ToolBar); 15 | expect(compiledControl.className).toContain("win-toolbar"); 16 | }); 17 | 18 | it("should use child ToolBarCommands", function () { 19 | var compiledControl = initControl("
" + 20 | "" + 21 | "" + 22 | "
"); 23 | 24 | expect(compiledControl.querySelectorAll(".win-command").length).toEqual(2); 25 | }); 26 | 27 | it("should use the closedDisplayMode attribute", function () { 28 | var compiledControl = initControl("
"); 29 | 30 | expect(compiledControl.winControl.closedDisplayMode).toEqual("full"); 31 | }); 32 | 33 | it("should use the onclose event handlers and opened attribute", function (done) { 34 | testEventChangesProperty({ 35 | control: "ToolBar", 36 | beforeChangeEvent: "beforeclose", 37 | afterChangeEvent: "afterclose", 38 | property: "opened", 39 | initialValue: true, 40 | finalValue: false 41 | }).then(function () { 42 | done(); 43 | }); 44 | }); 45 | 46 | it("should use the onopen event handlers and opened attribute", function (done) { 47 | testEventChangesProperty({ 48 | control: "ToolBar", 49 | beforeChangeEvent: "beforeopen", 50 | afterChangeEvent: "afteropen", 51 | property: "opened", 52 | initialValue: false, 53 | finalValue: true 54 | }).then(function () { 55 | done(); 56 | }); 57 | }); 58 | 59 | }); -------------------------------------------------------------------------------- /tests/ToolBarCommand.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("ToolBarCommand control directive tests", function () { 5 | 6 | sharedSetup(); 7 | 8 | it("should use initialize a ToolBar containing two child ToolBarCommands", function () { 9 | var compiledControl = initControl("
" + 10 | "" + 11 | "" + 12 | "
"); 13 | var winControl = compiledControl.winControl; 14 | 15 | expect(compiledControl.winControl).toBeDefined(); 16 | expect(compiledControl.winControl instanceof WinJS.UI.ToolBar); 17 | expect(compiledControl.className).toContain("win-toolbar"); 18 | expect(compiledControl.querySelectorAll(".win-command").length).toEqual(2); 19 | }); 20 | 21 | it("should use the id attribute on ToolBarCommands", function () { 22 | var compiledControl = initControl("
" + 23 | "" + 24 | "" + 25 | "
"); 26 | var commands = compiledControl.querySelectorAll(".win-command"); 27 | 28 | expect(commands[0].id).toEqual("command1"); 29 | expect(commands[1].id).toEqual("command2"); 30 | }); 31 | 32 | it("should use the label attribute on ToolBarCommands", function () { 33 | var compiledControl = initControl("
" + 34 | "" + 35 | "" + 36 | "
"); 37 | var commands = compiledControl.querySelectorAll(".win-command"); 38 | 39 | expect(commands[0].querySelector(".win-label").innerHTML).toEqual("command1"); 40 | expect(commands[1].querySelector(".win-label").innerHTML).toEqual("command2"); 41 | }); 42 | 43 | it("should use the disabled attribute on ToolBarCommands", function () { 44 | var compiledControl = initControl("
" + 45 | "" + 46 | "" + 47 | "
"); 48 | var commands = compiledControl.querySelectorAll(".win-command"); 49 | 50 | expect(commands[0].winControl.disabled).toBeTruthy(); 51 | expect(commands[1].winControl.disabled).toBeFalsy(); 52 | }); 53 | 54 | it("should use the extraClass attribute on ToolBarCommands", function () { 55 | var compiledControl = initControl("
" + 56 | "" + 57 | "
"); 58 | var commands = compiledControl.querySelectorAll(".win-command"); 59 | 60 | expect(commands[0].className).toContain("extraClass1"); 61 | }); 62 | 63 | it("should use the section attribute on ToolBarCommands", function () { 64 | var compiledControl = initControl("
" + 65 | "" + 66 | "" + 67 | "
"); 68 | var commands = compiledControl.querySelectorAll(".win-command"); 69 | 70 | expect(commands[0].winControl.section).toEqual("primary"); 71 | expect(commands[1].winControl.section).toEqual("secondary"); 72 | }); 73 | 74 | }); 75 | -------------------------------------------------------------------------------- /tests/Tooltip.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | describe("Tooltip control directive tests", function () { 5 | 6 | sharedSetup(); 7 | 8 | it("should initialize a simple Tooltip", function () { 9 | var compiledControl = initControl("
"); 10 | 11 | expect(compiledControl.winControl).toBeDefined(); 12 | expect(compiledControl.winControl instanceof WinJS.UI.Tooltip); 13 | }); 14 | 15 | it("should initialize the infotip attribute", function () { 16 | var compiledControl = initControl("
"); 17 | 18 | expect(compiledControl.winControl.infotip).toBeTruthy(); 19 | }); 20 | 21 | it("should initialize the placement attribute", function () { 22 | var compiledControl = initControl("
"); 23 | 24 | expect(compiledControl.winControl.placement).toEqual("right"); 25 | }); 26 | 27 | }); 28 | --------------------------------------------------------------------------------