diff --git a/.bowerrc b/.bowerrc deleted file mode 100644 index 9b2abeb..0000000 --- a/.bowerrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "directory" : "bower_components" -} diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..ad81109 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,18 @@ +{ + "parser": "babel-eslint", + "extends": [ + "standard" + ], + "env": { + "jasmine": true + }, + "rules": { + // overrides of the standard style + "curly": [2, "all"], + "indent": [2, 4], + "max-len": [2, 100, 4], + "semi": [2, "always"], + "space-before-function-paren": [2, {"anonymous": "always", "named": "never"}], + "wrap-iife": [2, "outside"] + } +} diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 0a91f75..0000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Automatically normalize line endings for all text-based files -* text=auto diff --git a/.gitignore b/.gitignore index 7bf6eb1..44d646d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -bower_components node_modules +dist/ diff --git a/.travis.yml b/.travis.yml index fde32f5..9ce799e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,20 @@ +sudo: false language: node_js +cache: + directories: + - node_modules +notifications: + email: false node_js: - - "0.10" + - '4' +before_install: + - npm i -g npm@^2.0.0 before_script: + - npm prune - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start +after_success: + - npm run semantic-release +branches: + except: + - "/^v\\d+\\.\\d+\\.\\d+$/" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 673468c..f2490c9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,6 +8,8 @@ the developers managing and developing this open source project. In return, they should reciprocate that respect in addressing your issue or assessing patches and features. +By contributing to this repository, including using the issue tracker, you agree to adhere to Twitter's [Open Source Code of Conduct][coc]. + ## Using the issue tracker diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index 0c48074..0000000 --- a/Gruntfile.js +++ /dev/null @@ -1,24 +0,0 @@ -module.exports = function (grunt) { - - require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks); - - grunt.initConfig({ - bump: { - options: { - files: [ - 'package.json', - 'bower.json' - ], - commit: true, - commitMessage: 'v%VERSION%', - commitFiles: ['-a'], - createTag: true, - tagName: 'v%VERSION%', - tagMessage: 'v%VERSION%', - push: true, - pushTo: 'origin', - gitDescribeOptions: '--tags --always --abbrev=1 --dirty=-d' - } - } - }); -}; diff --git a/README.md b/README.md index 19b3680..f58bef8 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This construct supports trees of components because, if the child also mixes in ## Installation ```bash -bower install --save flight-with-child-components +npm install --save flight-with-child-components ``` ## Example @@ -44,17 +44,13 @@ function parentComponent() { ## Development -Install the Node.js and client-side dependencies by running the following -commands in the repo's root directory. +To develop this module, clone the repository and run: -```bash -npm install +``` +$ npm install && npm test ``` -To continuously run the tests in Chrome during development, just run: - -```bash -npm run watch-test +If the tests pass, you have a working environment. You shouldn't need any external dependencies. ``` ## Contributing to this project diff --git a/bower.json b/bower.json deleted file mode 100644 index 4e30908..0000000 --- a/bower.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "flight-with-child-components", - "version": "0.2.4", - "main": "lib/flight-with-child-components.js", - "dependencies": { - "flight": ">=1.1.4" - }, - "license": "MIT", - "devDependencies": { - "jasmine-flight": "~3.0.0" - }, - "ignore": [ - ".*", - "CONTRIBUTING.md", - "CHANGELOG.md", - "test", - "package.json", - "karma.conf.js", - "Gruntfile.js" - ] -} diff --git a/config/constants.js b/config/constants.js new file mode 100644 index 0000000..f4125af --- /dev/null +++ b/config/constants.js @@ -0,0 +1,9 @@ +var path = require('path'); + +var ROOT_DIRECTORY = path.resolve(__dirname, '..'); +var BUILD_DIRECTORY = path.resolve(ROOT_DIRECTORY, 'dist'); + +module.exports = { + ROOT_DIRECTORY: ROOT_DIRECTORY, + BUILD_DIRECTORY: BUILD_DIRECTORY +}; diff --git a/config/karma.config.js b/config/karma.config.js new file mode 100644 index 0000000..b3ac74d --- /dev/null +++ b/config/karma.config.js @@ -0,0 +1,48 @@ +'use strict'; + +var constants = require('./constants'); +var webpackConfig = require('./webpack.config.test'); +// entry is determined by karma config 'files' array +webpackConfig.entry = {}; + +module.exports = function (config) { + config.set({ + basePath: constants.ROOT_DIRECTORY, + browsers: [ process.env.TRAVIS ? 'Firefox' : 'Chrome' ], + browserNoActivityTimeout: 60000, + client: { + captureConsole: true, + useIframe: true + }, + files: [ + 'node_modules/jquery/dist/jquery.min.js', + 'node_modules/jasmine-jquery/lib/jasmine-jquery.js', + 'src/specs.context.js' + ], + frameworks: [ + 'jasmine' + ], + plugins: [ + 'karma-chrome-launcher', + 'karma-firefox-launcher', + 'karma-jasmine', + 'karma-sourcemap-loader', + 'karma-webpack' + ], + preprocessors: { + 'src/specs.context.js': [ 'webpack', 'sourcemap' ] + }, + reporters: [ 'dots' ], + singleRun: true, + webpack: webpackConfig, + webpackMiddleware: { + stats: { + assetsSort: 'name', + colors: true, + children: false, + chunks: false, + modules: false + } + } + }); +}; diff --git a/config/webpack.config.js b/config/webpack.config.js new file mode 100644 index 0000000..011f186 --- /dev/null +++ b/config/webpack.config.js @@ -0,0 +1,46 @@ +var webpack = require('webpack'); + +var DedupePlugin = webpack.optimize.DedupePlugin; +var OccurenceOrderPlugin = webpack.optimize.OccurenceOrderPlugin; +var UglifyJsPlugin = webpack.optimize.UglifyJsPlugin; + +var plugins = [ + new DedupePlugin(), + new OccurenceOrderPlugin() +]; + +if (process.env.NODE_ENV === 'publish') { + plugins.push( + new UglifyJsPlugin({ + compress: { + dead_code: true, + drop_console: true, + screw_ie8: true, + warnings: true + } + }) + ); +} + +module.exports = { + entry: './src', + module: { + loaders: [ + { + test: /\.jsx?$/, + exclude: /node_modules/, + loader: 'babel-loader' + } + ] + }, + resolve: { + alias: { + flight: 'flightjs' + } + }, + output: { + path: './dist', + filename: 'flight-with-child-components.js' + }, + plugins: plugins +}; diff --git a/config/webpack.config.publish.js b/config/webpack.config.publish.js new file mode 100644 index 0000000..886fd64 --- /dev/null +++ b/config/webpack.config.publish.js @@ -0,0 +1,11 @@ +var constants = require('./constants'); +var baseConfig = require('./webpack.config'); + +module.exports = Object.assign(baseConfig, { + output: { + library: 'flight-with-child-components', + filename: 'flight-with-child-components.js', + libraryTarget: 'umd', + path: constants.BUILD_DIRECTORY + } +}); diff --git a/config/webpack.config.test.js b/config/webpack.config.test.js new file mode 100644 index 0000000..fd13dad --- /dev/null +++ b/config/webpack.config.test.js @@ -0,0 +1,5 @@ +var baseConfig = require('./webpack.config'); + +module.exports = Object.assign(baseConfig, { + devtool: 'inline-source-map' +}); diff --git a/karma.conf.js b/karma.conf.js deleted file mode 100644 index a14e38c..0000000 --- a/karma.conf.js +++ /dev/null @@ -1,57 +0,0 @@ -// Karma configuration file -// -// For all available config options and default values, see: -// https://github.com/karma-runner/karma/blob/stable/lib/config.js#L54 - -module.exports = function (config) { - 'use strict'; - - config.set({ - // base path, that will be used to resolve files and exclude - basePath: '', - - frameworks: [ - 'jasmine', - 'requirejs' - ], - - // list of files / patterns to load in the browser - files: [ - // loaded without require - 'bower_components/jquery/dist/jquery.min.js', - 'bower_components/jasmine-jquery/lib/jasmine-jquery.js', - 'bower_components/jasmine-flight/lib/jasmine-flight.js', - - // loaded with require - {pattern: 'bower_components/flight/**/*.js', included: false}, - {pattern: 'lib/**/*.js', included: false}, - {pattern: 'test/spec/**/*.spec.js', included: false}, - - 'test/test-main.js' - ], - - // enable / disable watching file and executing tests whenever any file changes - // CLI --auto-watch --no-auto-watch - autoWatch: true, - - // Start these browsers - // CLI --browsers Chrome, Firefox, Safari - browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'], - - // If browser does not capture in given timeout [ms], kill it - // CLI --capture-timeout 5000 - captureTimeout: 20000, - - // list of files to exclude - exclude: [], - - // use dots reporter, as travis terminal does not support escaping sequences - // possible values: 'dots', 'progress' - // CLI --reporters progress - reporters: [process.env.TRAVIS ? 'dots' : 'progress'], - - // Auto run tests on start (when browsers are captured) and exit - // CLI --single-run --no-single-run - singleRun: false - }); -}; diff --git a/lib/flight-with-child-components.js b/lib/flight-with-child-components.js deleted file mode 100644 index 148142d..0000000 --- a/lib/flight-with-child-components.js +++ /dev/null @@ -1,79 +0,0 @@ -/** - * withChildComponents - * - * See the README.md for up-to-date docs. - */ -define(function () { - 'use strict'; - - var teardownEventCount = 0; - - function withBoundLifecycle() { - // Use deprecated defaultAttrs() only if necessary - var defineDefaultAttributes = this.attrDef? this.attributes: this.defaultAttrs; - defineDefaultAttributes.call(this, { - teardownOn: '' - }); - - /** - * If we were given a teardownOn event then listen out for it to teardown. - */ - this.after('initialize', function () { - if (this.attr.teardownOn) { - if (this.attr.teardownOn === this.childTeardownEvent) { - throw new Error('Component initialized to listen for its own teardown event.'); - } - this.on(document, this.attr.teardownOn, function () { - this.teardown(); - }); - } - }); - } - - function withChildComponents() { - /** - * Give every component that uses this mixin a new, unique childTeardownEvent - */ - this.before('initialize', function () { - this.childTeardownEvent = - this.childTeardownEvent || - withChildComponents.nextTeardownEvent(); - }); - - /** - * Before this component's teardown, tell all the children to teardown - */ - this.before('teardown', function () { - this.trigger(this.childTeardownEvent); - }); - - /** - * Utility method for attaching a component with teardownOn. - * - * Takes Component (with attachTo method) plus destination and attrs arguments, which should - * be the same as in a normal attachTo call. - */ - this.attachChild = function (Component, destination, attrs) { - attrs = attrs || {}; - if (!attrs.teardownOn) { - attrs.teardownOn = this.childTeardownEvent; - } - var mixins = Component.prototype.mixedIn || []; - var isMixedIn = (mixins.indexOf(withBoundLifecycle) > -1) ? true : false; - (isMixedIn ? - Component : - Component.mixin(withBoundLifecycle)).attachTo(destination, attrs); - }; - - } - - withChildComponents.nextTeardownEvent = function () { - teardownEventCount += 1; - return '_teardownEvent' + teardownEventCount; - }; - - // Export the child lifecycle mixin - withChildComponents.withBoundLifecycle = withBoundLifecycle; - - return withChildComponents; -}); diff --git a/package.json b/package.json index 2b425cb..245cfe5 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,69 @@ { "name": "flight-with-child-components", - "version": "0.2.4", + "description": "A Flight mixin for nesting Flight components.", + "main": "dist/flght-with-child-components.js", + "scripts": { + "build": "rm -rf ./dist && NODE_ENV=publish webpack --config config/webpack.config.publish.js --sort-assets-by --progress", + "lint": "eslint config src", + "lint:fix": "eslint --fix config src", + "prepublish": "npm run build", + "specs": "NODE_ENV=test karma start config/karma.config.js", + "specs:watch": "npm run specs -- --no-single-run", + "test": "npm run specs && npm run lint", + "semantic-release": "semantic-release pre && npm publish && semantic-release post" + }, "devDependencies": { - "bower": "^1.3.3", - "grunt": "~0.4.1", - "grunt-bump": "latest", - "karma": "~0.12.19", - "karma-cli": "0.0.4", - "karma-jasmine": "~0.2.0", - "karma-requirejs": "~0.2.2", - "karma-chrome-launcher": "~0.1.4", - "karma-firefox-launcher": "~0.1.3", - "matchdep": "latest" + "babel-core": "^5.8.24", + "babel-eslint": "^4.1.1", + "babel-loader": "^5.3.2", + "babel-plugin-typecheck": "^1.2.0", + "babel-runtime": "^5.8.20", + "chai": "^3.2.0", + "eslint": "^1.3.1", + "eslint-config-standard": "^4.3.1", + "eslint-config-standard-react": "^1.0.4", + "eslint-plugin-react": "^3.3.1", + "eslint-plugin-standard": "^1.3.0", + "flightjs": "^1.5.1", + "immutable": "^3.7.5", + "jasmine-core": "^2.3.4", + "jasmine-flight": "^4.0.0", + "jasmine-jquery": "^2.1.1", + "jquery": "^2.1.4", + "karma": "^0.13.9", + "karma-chrome-launcher": "^0.2.0", + "karma-cli": "^0.1.0", + "karma-firefox-launcher": "^0.1.6", + "karma-jasmine": "^0.3.6", + "karma-mocha": "^0.2.0", + "karma-sourcemap-loader": "^0.3.5", + "karma-webpack": "^1.7.0", + "mocha": "^2.3.2", + "object-assign": "^4.0.1", + "semantic-release": "^4.3.5", + "webpack": "^1.12.1" }, - "scripts": { - "install": "bower install", - "test": "karma start --single-run", - "watch-test": "karma start" - } + "peerDependencies": { + "flightjs": "^1.5.1" + }, + "repository": { + "type": "git", + "url": "https://github.com/flightjs/flight-with-child-components.git" + }, + "keywords": [ + "flight", + "flightjs", + "components", + "nesting", + "flight-toolbox" + ], + "contributors": [ + "Tom Ashworth ", + "Andy Hume " + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/flightjs/flight-with-child-components/issues" + }, + "homepage": "https://github.com/flightjs/flight-with-child-components" } diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..bca9f85 --- /dev/null +++ b/src/index.js @@ -0,0 +1,75 @@ +/** + * withChildComponents + * + * See the README.md for up-to-date docs. + */ + +var teardownEventCount = 0; + +export function withBoundLifecycle() { + // Use deprecated defaultAttrs() only if necessary + var defineDefaultAttributes = (this.attrDef ? this.attributes : this.defaultAttrs); + defineDefaultAttributes.call(this, { + teardownOn: '' + }); + + /** + * If we were given a teardownOn event then listen out for it to teardown. + */ + this.after('initialize', function () { + if (this.attr.teardownOn) { + if (this.attr.teardownOn === this.childTeardownEvent) { + throw new Error('Component initialized to listen for its own teardown event.'); + } + this.on(document, this.attr.teardownOn, function () { + this.teardown(); + }); + } + }); +} + +export default function withChildComponents() { + /** + * Give every component that uses this mixin a new, unique childTeardownEvent + */ + this.before('initialize', function () { + this.childTeardownEvent = + this.childTeardownEvent || + withChildComponents.nextTeardownEvent(); + }); + + /** + * Before this component's teardown, tell all the children to teardown + */ + this.before('teardown', function () { + this.trigger(this.childTeardownEvent); + }); + + /** + * Utility method for attaching a component with teardownOn. + * + * Takes Component (with attachTo method) plus destination and attrs arguments, which should + * be the same as in a normal attachTo call. + */ + this.attachChild = function (Component, destination, attrs) { + attrs = attrs || {}; + if (!attrs.teardownOn) { + attrs.teardownOn = this.childTeardownEvent; + } + var mixins = Component.prototype.mixedIn || []; + var isMixedIn = (mixins.indexOf(withBoundLifecycle) > -1); + var ComponentWithMixin = ( + isMixedIn + ? Component + : Component.mixin(withBoundLifecycle) + ); + ComponentWithMixin.attachTo(destination, attrs); + }; +} + +withChildComponents.nextTeardownEvent = function () { + teardownEventCount += 1; + return '_teardownEvent' + teardownEventCount; +}; + +withChildComponents.withBoundLifecycle = withBoundLifecycle; diff --git a/src/specs.context.js b/src/specs.context.js new file mode 100644 index 0000000..0c285e5 --- /dev/null +++ b/src/specs.context.js @@ -0,0 +1,14 @@ +/** + * Since we use webpack-specific features in our modules (e.g., loaders, + * plugins, adding CSS to the dependency graph), we must use webpack to build a + * test bundle. + * + * This module creates a context of all the unit test files (as per the unit + * test naming convention). It's used as the webpack entry file for unit tests. + * + * See: https://github.com/webpack/docs/wiki/context + */ + +const specsContext = require.context('.', true, /.+\.spec\.js$/); +specsContext.keys().forEach(specsContext); +module.exports = specsContext; diff --git a/src/with-child-components.spec.js b/src/with-child-components.spec.js new file mode 100644 index 0000000..1df7ea9 --- /dev/null +++ b/src/with-child-components.spec.js @@ -0,0 +1,147 @@ +import { component as defineComponent } from 'flight'; +import withChildComponents from '.'; + +describe('withChildComponents', function () { + var Component; + var ChildComponent; + var ComponentWithoutMixin; + var FakeComponent; + + // Initialize the component and attach it to the DOM + beforeEach(function () { + window.outerDiv = document.createElement('div'); + window.innerDiv = document.createElement('div'); + window.otherInnerDiv = document.createElement('div'); + window.outerDiv.appendChild(window.innerDiv); + window.outerDiv.appendChild(window.otherInnerDiv); + window.outerDiv.id = 'outerDiv'; + window.innerDiv.id = 'innerDiv'; + window.otherInnerDiv.id = 'otherInnerDiv'; + document.body.appendChild(window.outerDiv); + + window.childDidTeardown = false; + window.otherChildDidTeardown = false; + + Component = defineComponent(function parentComponent() {}).mixin(withChildComponents); + ChildComponent = defineComponent(function childComponent() { + this.defaultAttrs({ + teardownAttr: '' + }); + this.before('teardown', function () { + window[this.attr.teardownAttr] = true; + }); + }); + ComponentWithoutMixin = defineComponent(function componentWithoutMixin() {}); + FakeComponent = function () {}; + FakeComponent.prototype = { + mixedIn: [] + }; + FakeComponent.mixin = function () { + return FakeComponent; + }; + FakeComponent.attachTo = jasmine.createSpy(); + }); + + afterEach(function () { + document.body.removeChild(window.outerDiv); + window.outerDiv = null; + window.innerDiv = null; + window.otherInnerDiv = null; + Component.teardownAll(); + ChildComponent.teardownAll(); + ComponentWithoutMixin.teardownAll(); + }); + + it('should get a childTeardownEvent', function () { + var component = new Component(); + component.initialize(window.outerDiv); + expect(component.childTeardownEvent).toBeDefined(); + }); + + it('should teardown the child when torn down', function () { + var parent = new Component(); + parent.initialize(window.outerDiv); + parent.attachChild(ChildComponent, window.innerDiv, { + teardownAttr: 'childDidTeardown' + }); + parent.teardown(); + expect(window.childDidTeardown).toBe(true); + }); + + it('should teardown the child when torn down if component uses new attributes', function () { + var parent = new Component(); + parent.initialize(window.outerDiv); + var ChildComponentWithNewAttributes = defineComponent( + function childComponentWithNewAttributes() { + // New method of attribute definition + this.attributes({ + teardownAttr: '' + }); + this.before('teardown', function () { + window[this.attr.teardownAttr] = true; + }); + } + ); + parent.attachChild(ChildComponentWithNewAttributes, window.innerDiv, { + teardownAttr: 'childDidTeardown' + }); + parent.teardown(); + expect(window.childDidTeardown).toBe(true); + }); + + it('should teardown all children when torn down', function () { + var parent = new Component(); + parent.initialize(window.outerDiv); + parent.attachChild(ChildComponent, window.innerDiv, { + teardownAttr: 'childDidTeardown' + }); + parent.attachChild(ChildComponent, document, { + teardownAttr: 'otherChildDidTeardown' + }); + parent.teardown(); + expect(window.childDidTeardown).toBe(true); + expect(window.otherChildDidTeardown).toBe(true); + }); + + describe('attachChild', function () { + it('should attach child with teardownOn', function () { + var component = new Component(); + component.initialize(window.outerDiv); + component.attachChild(FakeComponent, '.my-selector', { test: true }); + expect(FakeComponent.attachTo).toHaveBeenCalledWith('.my-selector', { + test: true, + teardownOn: component.childTeardownEvent + }); + }); + it('should mix withBoundLifecycle into child', function () { + var component = new Component(); + component.initialize(window.outerDiv); + var spy = spyOn(ComponentWithoutMixin, 'mixin').and.callThrough(); + component.attachChild(ComponentWithoutMixin, '.my-selector', {}); + expect(spy).toHaveBeenCalledWith(withChildComponents.withBoundLifecycle); + }); + it('should not mix withBoundLifecycle twice', function () { + var component = new Component(); + component.initialize(window.outerDiv); + var ComponentWithBoundLifecyleMixin = ComponentWithoutMixin.mixin( + withChildComponents.withBoundLifecycle + ); + var spy = spyOn(ComponentWithBoundLifecyleMixin, 'mixin').and.callThrough(); + component.attachChild(ComponentWithBoundLifecyleMixin, '.my-selector', {}); + expect(spy).not.toHaveBeenCalledWith(withChildComponents.withBoundLifecycle); + }); + it('should not overwrite a passed teardownOn event', function () { + var component = new Component(); + component.initialize(window.outerDiv); + component.attachChild( + FakeComponent, + '.my-selector', + { test: true, teardownOn: 'someTeardownEvent' } + ); + expect(FakeComponent.attachTo).toHaveBeenCalledWith('.my-selector', { + test: true, + teardownOn: 'someTeardownEvent' + }); + }); + }); +}); diff --git a/test/spec/flight-with-child-components.spec.js b/test/spec/flight-with-child-components.spec.js deleted file mode 100644 index 302610e..0000000 --- a/test/spec/flight-with-child-components.spec.js +++ /dev/null @@ -1,150 +0,0 @@ -define(function (require) { - 'use strict'; - - var defineComponent = require('flight/lib/component'); - var withChildComponents = require('lib/flight-with-child-components'); - - describeMixin('lib/flight-with-child-components', function () { - - var Component; - var ChildComponent; - var ComponentWithoutMixin; - var FakeComponent; - - // Initialize the component and attach it to the DOM - beforeEach(function () { - window.outerDiv = document.createElement('div'); - window.innerDiv = document.createElement('div'); - window.otherInnerDiv = document.createElement('div'); - window.outerDiv.appendChild(window.innerDiv); - window.outerDiv.appendChild(window.otherInnerDiv); - window.outerDiv.id = 'outerDiv'; - window.innerDiv.id = 'innerDiv'; - window.otherInnerDiv.id = 'otherInnerDiv'; - document.body.appendChild(window.outerDiv); - - window.childDidTeardown = false; - window.otherChildDidTeardown = false; - - Component = defineComponent(function parentComponent() {}).mixin(withChildComponents); - ChildComponent = defineComponent(function childComponent() { - this.defaultAttrs({ - teardownAttr: '' - }); - this.before('teardown', function () { - window[this.attr.teardownAttr] = true; - }); - }); - ComponentWithoutMixin = defineComponent(function componentWithoutMixin() {}); - FakeComponent = function () {}; - FakeComponent.prototype = { - mixedIn: [] - }; - FakeComponent.mixin = function () { - return FakeComponent; - }; - FakeComponent.attachTo = jasmine.createSpy(); - }); - - afterEach(function () { - document.body.removeChild(window.outerDiv); - window.outerDiv = null; - window.innerDiv = null; - window.otherInnerDiv = null; - Component.teardownAll(); - ChildComponent.teardownAll(); - ComponentWithoutMixin.teardownAll(); - }); - - it('should get a childTeardownEvent', function () { - var component = new Component(); - component.initialize(window.outerDiv); - expect(component.childTeardownEvent).toBeDefined(); - }); - - it('should teardown the child when torn down', function () { - var parent = new Component(); - parent.initialize(window.outerDiv); - parent.attachChild(ChildComponent, window.innerDiv, { - teardownAttr: 'childDidTeardown' - }); - var parentEventSpy = spyOnEvent(document, parent.childTeardownEvent); - parent.teardown(); - expect(parentEventSpy).toHaveBeenTriggeredOn(document); - expect(window.childDidTeardown).toBe(true); - }); - - it('should teardown the child when torn down if component uses new attributes', function () { - var parent = new Component(); - parent.initialize(window.outerDiv); - var ChildComponentWithNewAttributes = defineComponent(function childComponentWithNewAttributes() { - // New method of attribute definition - this.attributes({ - teardownAttr: '' - }); - this.before('teardown', function () { - window[this.attr.teardownAttr] = true; - }); - }); - parent.attachChild(ChildComponentWithNewAttributes, window.innerDiv, { - teardownAttr: 'childDidTeardown' - }); - var parentEventSpy = spyOnEvent(document, parent.childTeardownEvent); - parent.teardown(); - expect(parentEventSpy).toHaveBeenTriggeredOn(document); - expect(window.childDidTeardown).toBe(true); - }); - - it('should teardown all children when torn down', function () { - var parent = new Component(); - parent.initialize(window.outerDiv); - parent.attachChild(ChildComponent, window.innerDiv, { - teardownAttr: 'childDidTeardown' - }); - parent.attachChild(ChildComponent, document, { - teardownAttr: 'otherChildDidTeardown' - }); - var parentEventSpy = spyOnEvent(document, parent.childTeardownEvent); - parent.teardown(); - expect(parentEventSpy).toHaveBeenTriggeredOn(document); - expect(window.childDidTeardown).toBe(true); - expect(window.otherChildDidTeardown).toBe(true); - }); - - describe('attachChild', function () { - it('should attach child with teardownOn', function () { - this.setupComponent(); - this.component.attachChild(FakeComponent, '.my-selector', { test: true }); - expect(FakeComponent.attachTo).toHaveBeenCalledWith('.my-selector', { - test: true, - teardownOn: this.component.childTeardownEvent - }); - }); - it('should mix withBoundLifecycle into child', function () { - this.setupComponent(); - var spy = spyOn(ComponentWithoutMixin, 'mixin').and.callThrough(); - this.component.attachChild(ComponentWithoutMixin, '.my-selector', {}); - expect(spy).toHaveBeenCalledWith(withChildComponents.withBoundLifecycle); - }); - it('should not mix withBoundLifecycle twice', function () { - this.setupComponent(); - var ComponentWithBoundLifecyleMixin = ComponentWithoutMixin.mixin( - withChildComponents.withBoundLifecycle - ); - var spy = spyOn(ComponentWithBoundLifecyleMixin, 'mixin').and.callThrough(); - this.component.attachChild(ComponentWithBoundLifecyleMixin, '.my-selector', {}); - expect(spy).not.toHaveBeenCalledWith(withChildComponents.withBoundLifecycle); - }); - it('should not overwrite a passed teardownOn event', function () { - this.setupComponent(); - this.component.attachChild(FakeComponent, '.my-selector', { test: true, teardownOn: 'someTeardownEvent' }); - expect(FakeComponent.attachTo).toHaveBeenCalledWith('.my-selector', { - test: true, - teardownOn: 'someTeardownEvent' - }); - }); - }); - - }); - -}); diff --git a/test/test-main.js b/test/test-main.js deleted file mode 100644 index e2f6eb7..0000000 --- a/test/test-main.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; - -var tests = Object.keys(window.__karma__.files).filter(function (file) { - return (/\.spec\.js$/.test(file)); -}); - -requirejs.config({ - // Karma serves files from '/base' - baseUrl: '/base', - - paths: { - 'flight': 'bower_components/flight' - }, - - // ask Require.js to load these files (all our tests) - deps: tests, - - // start test run, once Require.js is done - callback: window.__karma__.start -});