diff --git a/.gitignore b/.gitignore index 3c3629e..40f29d5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,6 @@ +.DS_Store node_modules +*.d.ts +*.js +*.map +!karma.conf.js \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..c83e942 --- /dev/null +++ b/.npmignore @@ -0,0 +1,3 @@ +.DS_Store +node_modules +test* \ No newline at end of file diff --git a/README.md b/README.md index 399c288..00de45c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# angularize [![Build Status](https://img.shields.io/circleci/project/bcherny/angularize.svg?branch=master&style=flat-square)](https://circleci.com/gh/bcherny/angularize) [![NPM](https://img.shields.io/npm/v/angularize.svg?style=flat-square)](https://www.npmjs.com/package/angularize) [![MIT](https://img.shields.io/npm/l/angularize.svg?style=flat-square)](https://opensource.org/licenses/MIT) +# angularize [![Build Status](https://img.shields.io/circleci/project/coatue/angularize.svg?branch=master&style=flat-square)](https://circleci.com/gh/coatue/angularize) [![NPM](https://img.shields.io/npm/v/angularize.svg?style=flat-square)](https://www.npmjs.com/package/angularize) [![Apache2](https://img.shields.io/npm/l/angularize.svg?style=flat-square)](https://opensource.org/licenses/Apache2) > Description diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000..f31fb57 --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,32 @@ +module.exports = function(config) { + config.set({ + basePath: '', + frameworks: ['source-map-support', 'browserify', 'jasmine'], + files: [ + './test.js' + ], + preprocessors: { + '*.js': ['browserify'] + }, + browserify: { + debug: true, + external: ['angular', 'angular-mocks'], + extensions: ['.js'], + transform: ['rollupify'] + }, + reporters: ['mocha'], + port: 9876, + colors: true, + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_WARN, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + concurrency: Infinity, + browserConsoleLogOptions: { + level: 'log', + terminal: true + }, + browserDisconnectTolerance: 30 + }) +} diff --git a/package.json b/package.json index 8190f2e..8023bcd 100644 --- a/package.json +++ b/package.json @@ -3,14 +3,17 @@ "version": "1.0.0", "description": "The easiest way to use React components in Angular 1", "main": "index.js", + "main:esnext": "index.es2015.js", "typings": "index.d.ts", "scripts": { - "build": "rm ./*.d.ts; tsc", + "build": "npm run clean; tsc -d -m es2015 && mv ./index.js ./index.es2015.js && tsc -m commonjs -t es5", + "clean": "rm ./*.d.ts; rm ./*.map; exit 0", "pretest": "npm run build", "prepublish": "npm test", - "test": "ava", - "tdd": "npm run watch & ava -w", - "watch": "tsc -w" + "test": "karma start --single-run", + "tdd": "npm-run-all -pr watch:*", + "watch:ts": "tsc -w", + "watch:test": "karma start" }, "repository": { "type": "git", @@ -24,9 +27,24 @@ }, "homepage": "https://github.com/coatue/angularize#readme", "devDependencies": { - "ava": "^0.18.2", + "@types/angular-mocks": "^1.5.9", + "@types/jasmine": "^2.5.43", + "@types/react-addons-test-utils": "^0.14.17", + "angular-mocks": "^1.6.2", + "jasmine": "^2.5.3", + "karma": "^1.5.0", + "karma-browserify": "^5.1.1", + "karma-chrome-launcher": "^2.0.0", + "karma-jasmine": "^1.1.0", + "karma-mocha-reporter": "^2.2.2", + "karma-source-map-support": "^1.2.0", + "ngimport": "^0.5.2", + "npm-run-all": "^4.0.2", + "react-addons-test-utils": "^15.4.2", + "rollupify": "^0.3.9", "tslint": "^4.4.2", - "typescript": "^2.2.1" + "typescript": "^2.2.1", + "watchify": "^3.9.0" }, "dependencies": { "@types/angular": "^1.6.7", diff --git a/test.tsx b/test.tsx index 2ba134e..be16204 100644 --- a/test.tsx +++ b/test.tsx @@ -1,28 +1,194 @@ +import { bootstrap, element as $, ICompileService, mock, module } from 'angular' +import 'angular-mocks' +import { $rootScope } from 'ngimport' import * as React from 'react' +import { Simulate } from 'react-addons-test-utils' import { angularize } from './' -interface Props { - foo: number - bar: string - baz: boolean[] -} - -class Test extends React.Component { +class TestOne extends React.Component { render() { return

Foo: {this.props.foo}

-

Bar: {this.props.bar}

-

Baz: {this.props.baz.join(',')}

+

Bar: {this.props.bar.join(',')}

+

this.props.baz(42)}>Baz

+ {this.props.children}
} + componentWillUnmount() {} } -const Test2: React.StatelessComponent = props => +const TestTwo: React.StatelessComponent = props =>

Foo: {props.foo}

-

Bar: {props.bar}

-

Baz: {props.baz.join(',')}

+

Bar: {props.bar.join(',')}

+

props.baz(42)}>Baz

+ {props.children}
-const TestAngular = angularize(Test, ['foo', 'bar', 'baz']) -const Test2Angular = angularize(Test2, ['foo', 'bar', 'baz']) \ No newline at end of file +const TestAngularOne = angularize(TestOne, ['foo', 'bar', 'baz']) +const TestAngularTwo = angularize(TestTwo, ['foo', 'bar', 'baz']) + +module('test', []) + .component('testAngularOne', TestAngularOne) + .component('testAngularTwo', TestAngularTwo) + +bootstrap($(), ['test']) + +interface Props { + bar: boolean[] + baz(value: number): any + foo: number +} + +describe('angularize', () => { + + let $compile: any + + beforeEach(() => { + mock.module('test') + mock.inject(function(_$compile_: ICompileService) { + $compile = _$compile_ + }) + }) + + it('should give an angular component', () => { + expect(TestAngularOne.bindings).not.toBe(undefined) + expect(TestAngularOne.controller).not.toBe(undefined) + }) + + describe('react classes', () => { + + it('should render', () => { + const scope = Object.assign($rootScope.$new(true), { + bar: [true, false], + baz: (value: number) => value + 1, + foo: 1 + }) + const element = $(``) + $compile(element)(scope) + $rootScope.$apply() + expect(element.find('p').length).toBe(3) + }) + + it('should update', () => { + const scope = Object.assign($rootScope.$new(true), { + bar: [true, false], + baz: (value: number) => value + 1, + foo: 1 + }) + const element = $(``) + $compile(element)(scope) + $rootScope.$apply() + expect(element.find('p').eq(1).text()).toBe('Bar: true,false') + scope.$apply(() => + scope.bar = [false, true, true] + ) + expect(element.find('p').eq(1).text()).toBe('Bar: false,true,true') + }) + + it('should destroy', () => { + const scope = Object.assign($rootScope.$new(true), { + bar: [true, false], + baz: (value: number) => value + 1, + foo: 1 + }) + const element = $(``) + $compile(element)(scope) + $rootScope.$apply() + spyOn(TestOne.prototype, 'componentWillUnmount') + scope.$destroy() + expect(TestOne.prototype.componentWillUnmount).toHaveBeenCalled() + }) + + it('should take callbacks', () => { + const baz = jasmine.createSpy('baz') + const scope = Object.assign($rootScope.$new(true), { + bar: [true, false], + baz, + foo: 1 + }) + const element = $(``) + $compile(element)(scope) + $rootScope.$apply() + Simulate.click(element.find('p').eq(2)[0]) + expect(baz).toHaveBeenCalledWith(42) + }) + + // TODO: support children + it('should not support children', () => { + const scope = Object.assign($rootScope.$new(true), { + bar: [true, false], + baz: (value: number) => value + 1, + foo: 1 + }) + const element = $(`Transcluded`) + $compile(element)(scope) + $rootScope.$apply() + expect(element.find('span').length).toBe(0) + }) + + }) + + describe('react stateless components', () => { + + it('should render', () => { + const scope = Object.assign($rootScope.$new(true), { + bar: [true, false], + baz: (value: number) => value + 1, + foo: 1 + }) + const element = $(``) + $compile(element)(scope) + $rootScope.$apply() + expect(element.find('p').length).toBe(3) + }) + + it('should update', () => { + const scope = Object.assign($rootScope.$new(true), { + bar: [true, false], + baz: (value: number) => value + 1, + foo: 1 + }) + const element = $(``) + $compile(element)(scope) + $rootScope.$apply() + expect(element.find('p').eq(1).text()).toBe('Bar: true,false') + scope.$apply(() => + scope.bar = [false, true, true] + ) + expect(element.find('p').eq(1).text()).toBe('Bar: false,true,true') + }) + + // TODO: figure out how to test this + xit('should destroy', () => {}) + + it('should take callbacks', () => { + const baz = jasmine.createSpy('baz') + const scope = Object.assign($rootScope.$new(true), { + bar: [true, false], + baz, + foo: 1 + }) + const element = $(``) + $compile(element)(scope) + $rootScope.$apply() + Simulate.click(element.find('p').eq(2)[0]) + expect(baz).toHaveBeenCalledWith(42) + }) + + // TODO: support children + it('should not support children', () => { + const scope = Object.assign($rootScope.$new(true), { + bar: [true, false], + baz: (value: number) => value + 1, + foo: 1 + }) + const element = $(`Transcluded`) + $compile(element)(scope) + $rootScope.$apply() + expect(element.find('span').length).toBe(0) + }) + + }) + +}) diff --git a/tsconfig.json b/tsconfig.json index aac660d..6ac9943 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,11 @@ { "compilerOptions": { "allowSyntheticDefaultImports": true, - "declaration": true, + "declaration": false, "forceConsistentCasingInFileNames": true, "jsx": "react", "lib": [ + "dom", "es2015", "es2016.array.include" ], @@ -18,5 +19,9 @@ "sourceMap": true, "strictNullChecks": true, "target": "es6" - } + }, + "files": [ + "index.tsx", + "test.tsx" + ] } \ No newline at end of file