diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..83903b3 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +language: node_js +node_js: + - '6.9.1' +script: node_modules/karma/bin/karma start karma.conf.js --single-run +before_install: + - export CHROME_BIN=chromium-browser + - export DISPLAY=:99.0 + - sh -e /etc/init.d/xvfb start + - npm install -g bower +before_script: + - npm install + - bower install \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..f7d008b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible Node.js debug attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "attach", + "name": "Attach to Process", + "port": 5858, + "outFiles": [], + "sourceMaps": true + } + ] +} \ No newline at end of file diff --git a/bower.json b/bower.json index 8fdcce6..2fa4acb 100644 --- a/bower.json +++ b/bower.json @@ -19,5 +19,9 @@ ], "dependencies": { "angular-scroll-rtl": "^0.1.1" + }, + "devDependencies": { + "angular": "^1.5.8", + "angular-mocks": "^1.5.8" } } diff --git a/dist/tileview.js b/dist/tileview.js index 97a367d..74f7879 100644 --- a/dist/tileview.js +++ b/dist/tileview.js @@ -400,7 +400,7 @@ else { if (!animationFrameRequested) { animationFrameRequested = true; - requestAnimationFrame(update); + $window.requestAnimationFrame(update); } } } diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000..9baffd6 --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,101 @@ +// Karma configuration +// Generated on Tue Nov 22 2016 21:02:47 GMT+0100 (CET) + +module.exports = function (config) { + var configuration = { + + // base path that will be used to resolve all patterns (eg. files, exclude) + basePath: '', + + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ['jasmine'], + + + // list of files / patterns to load in the browser + files: [ + 'bower_components/angular/angular.js', + 'bower_components/angular-mocks/angular-mocks.js', + 'bower_components/angular-scroll-rtl/dist/angular-scroll-rtl.js', + 'node_modules/lodash/lodash.js', + 'dist/tileview.js', + 'dist/tileview.css', + 'test/**/*.js' + ], + + + // list of files to exclude + exclude: [ + ], + + + // preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + preprocessors: { + 'test/**/*.js': ['babel'] + }, + babelPreprocessor: { + options: { + presets: ['es2015'], + sourceMap: 'inline' + }, + filename: function (file) { + return file.originalPath.replace(/\.js$/, '.es5.js'); + }, + sourceFileName: function (file) { + return file.originalPath; + } + }, + + + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ['progress'], + + + // web server port + port: 9876, + + + // enable / disable colors in the output (reporters and logs) + colors: true, + + + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_INFO, + + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: true, + + + // start these browsers + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: ['Chrome'], + + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: false, + + // Concurrency level + // how many browser should be started simultaneous + concurrency: Infinity, + + customLaunchers: { + Chrome_travis_ci: { + base: 'Chrome', + flags: ['--no-sandbox'] + } + } + }; + + if (process.env.TRAVIS) { + configuration.browsers = ['Chrome_travis_ci']; + } + + config.set(configuration); +} diff --git a/package.json b/package.json index 21ec1d5..1345b0f 100644 --- a/package.json +++ b/package.json @@ -17,11 +17,18 @@ }, "homepage": "https://github.com/tinydesk/angular-tileview#readme", "devDependencies": { + "babel-preset-es2015": "^6.18.0", "gulp": "^3.9.1", "gulp-angular-templatecache": "^1.8.0", "gulp-concat": "^2.6.0", "gulp-less": "^3.0.5", "gulp-typescript": "^2.13.0", + "jasmine": "^2.5.2", + "karma": "^1.3.0", + "karma-babel-preprocessor": "^6.0.1", + "karma-chrome-launcher": "^2.0.0", + "karma-jasmine": "^1.0.2", + "lodash": "^4.17.2", "streamqueue": "^1.1.1" } } diff --git a/src/tileview.ts b/src/tileview.ts index 87527c7..afa1920 100644 --- a/src/tileview.ts +++ b/src/tileview.ts @@ -450,7 +450,7 @@ declare const angular: any; } else { if (!animationFrameRequested) { animationFrameRequested = true; - requestAnimationFrame(update); + $window.requestAnimationFrame(update); } } } diff --git a/test/integration.js b/test/integration.js new file mode 100644 index 0000000..79c27de --- /dev/null +++ b/test/integration.js @@ -0,0 +1,130 @@ +const getName = index => 'Some name ' + index; + +const createItems = count => _.range(count).map(i => ({ + name: getName(i) +})); + +const getRows = el => el.children().children().children(); + +const log = items => { + _.range(items.length).forEach(i => console.log(items.eq(i).text())); +} + +const transformRegex = /translate3d\(0px, (.*)px, 0px\)/; +const getTranslation = row => parseInt(row.css('transform').match(transformRegex)[1]); + +const checkRows = (rows, spec, offset = 0) => { + _.range(rows.length).forEach(i => { + const r = rows.eq(i); + const s = spec[i]; + const expectedTranslation = _.isNumber(s) ? s : s[0]; + expect(getTranslation(r)).toBe(expectedTranslation); + + _.range(Math.min(r.children().length, s[1] || 1000)).forEach(j => { + const item = r.children().eq(j); + expect(item.text()).toBe(getName(j + expectedTranslation / 100 * 4)); + expect(item.css('display')).toBe('inline-block'); + }); + + if (s[1] !== undefined) { + _.range(s[1], r.children().length).forEach(j => { + const item = r.children().eq(j); + expect(item.css('display')).toBe('none'); + }) + } + + }); +}; + +describe('Vertical mode', () => { + + let $compile, $rootScope, $timeout; + + const tileview = (items, opts) => { + $rootScope.options = _.defaults(opts, { + tileSize: { + width: 100, + height: 100 + }, + templateUrl: 'item.tpl.html', + overflow: 0 + }); + $rootScope.items = items; + const el = $compile('
')($rootScope); + angular.element(document.body).append(el); + $rootScope.$digest(); + return el.children(); + }; + + beforeAll(() => { + angular.module('td.tileview').run(['$templateCache', function ($templateCache) { + $templateCache.put('item.tpl.html', '

{{ item.name }}

'); + }]); + }); + + beforeEach(module('td.tileview')); + + beforeEach(function () { + module(function ($provide) { + $provide.value('$window', { + addEventListener: () => { }, + removeEventListener: () => { }, + requestAnimationFrame: fn => fn() + }); + }); + }); + + beforeEach(inject((_$compile_, _$rootScope_, _$timeout_) => { + $compile = _$compile_; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + })); + + it('should render empty tile view', () => { + const el = tileview(); + expect(el.children().hasClass('tile-view')).toBe(true); + expect(el.children().children().hasClass('item-container')).toBe(true); + expect(getRows(el).length).toBe(0); + }); + + describe('default options', () => { + + let scrollContainer, rows, items; + + const scroll = to => { + scrollContainer[0].scrollTop = 300; + scrollContainer.triggerHandler('scroll'); + }; + + beforeEach(() => { + const tv = tileview(createItems(25)); + rows = getRows(tv); + scrollContainer = tv.children(); + items = rows.children(); + }) + + it('should render correct number of rows', () => { + expect(rows.length).toBe(4); + }); + + it('should render the correct number of items', () => { + expect(rows.children().length).toBe(4 * 4); + }); + + it('should render the correct data', () => { + checkRows(rows, [0, 100, 200, 300]); + }); + + it('should render the correct data after scrolling', () => { + scroll(300); + checkRows(rows, [400, 500, [600, 1], 300]); + }); + + }); + + it('should render overflow rows', () => { + const rows = getRows(tileview(createItems(25), { overflow: 2 })); + expect(rows.length).toBe(8); + }); + +}); \ No newline at end of file