diff --git a/karma.conf.js b/karma.conf.js index 1f3af9aa6..531630448 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -30,7 +30,7 @@ module.exports = function(config) { // Application resources 'static/js/**/*.ts', - // Tests + // Tests utils 'static/test/**/*.js', ], exclude: [ diff --git a/static/js/quay.config.ts b/static/js/quay.config.ts index cf280ff11..110f926cb 100644 --- a/static/js/quay.config.ts +++ b/static/js/quay.config.ts @@ -1,4 +1,5 @@ import * as Raven from 'raven-js'; +import { RouteBuilder } from './route-builder/route-builder.service'; quayConfig.$inject = [ @@ -57,10 +58,9 @@ export function quayConfig( // index rule to make sure that deep links directly deep into the app continue to work. // WARNING WARNING WARNING - var layoutProfile = 'layout'; - console.log('Using layout profile: ' + layoutProfile); + var layoutProfile: string = 'layout'; - var routeBuilder = new RouteBuilderProvider.$get()($routeProvider, pages, [ + var routeBuilder: RouteBuilder = new RouteBuilderProvider.$get()($routeProvider, pages, [ // Start with the old pages (if we asked for it). {id: 'old-layout', templatePath: '/static/partials/'}, diff --git a/static/js/quay.module.ts b/static/js/quay.module.ts index 59ac95ff9..3145ffc12 100644 --- a/static/js/quay.module.ts +++ b/static/js/quay.module.ts @@ -3,6 +3,7 @@ import { quayConfig } from './quay.config'; import quayPages from './quay-pages.module'; import quayRun from './quay.run'; import { angularViewArrayFactory } from './services/angular-view-array/angular-view-array'; +import { routeBuilderFactory } from './route-builder/route-builder.service.impl'; import namePatterns from './constants/name-patterns.constant'; @@ -46,6 +47,7 @@ export default angular .module('quay', quayDependencies) .config(quayConfig) .constant('namePatterns', namePatterns) + .factory('RouteBuilder', routeBuilderFactory) .factory('AngularViewArray', angularViewArrayFactory) .run(quayRun) .name; \ No newline at end of file diff --git a/static/js/route-builder/route-builder.js b/static/js/route-builder/route-builder.js deleted file mode 100644 index 8a94706eb..000000000 --- a/static/js/route-builder/route-builder.js +++ /dev/null @@ -1,57 +0,0 @@ -(function() { - 'use strict'; - - angular - .module("quay") - .factory('RouteBuilder', factory); - - factory.$inject = [ - - ]; - - function factory() { - function RouteBuilder(routeProvider, pages, profiles, currentProfile) { - this.routeProvider = routeProvider; - this.pages = pages; - this.profiles = profiles; - - for (var i = 0; i < profiles.length; ++i) { - if (profiles[i].id == currentProfile) { - this.profiles = this.profiles.slice(i); - break; - } - } - } - - RouteBuilder.prototype.otherwise = function(options) { - this.routeProvider.otherwise(options); - }; - - RouteBuilder.prototype.route = function(path, pagename) { - // Lookup the page, matching our lists of profiles. - var pair = this.pages.get(pagename, this.profiles); - if (!pair) { - throw Error('Unknown page: ' + pagename); - } - - // Create the route. - var foundProfile = pair[0]; - var page = pair[1]; - var templateUrl = foundProfile.templatePath + page.templateName; - - var options = jQuery.extend({}, page.flags || {}); - options['templateUrl'] = templateUrl; - options['reloadOnSearch'] = false; - options['controller'] = page.controller; - - this.routeProvider.when(path, options); - - return this; - }; - - return function(routeProvider, pages, profiles, currentProfile) { - return new RouteBuilder(routeProvider, pages, profiles, currentProfile); - } - } - -})(); \ No newline at end of file diff --git a/static/js/route-builder/route-builder.service.impl.ts b/static/js/route-builder/route-builder.service.impl.ts new file mode 100644 index 000000000..f39769965 --- /dev/null +++ b/static/js/route-builder/route-builder.service.impl.ts @@ -0,0 +1,47 @@ +import { RouteBuilder } from './route-builder.service'; + + +export function routeBuilderFactory() { + return (routeProvider, pages, profiles, currentProfile): RouteBuilder => { + return new RouteBuilderImpl(routeProvider, pages, profiles, currentProfile); + } +} + + +export class RouteBuilderImpl implements RouteBuilder { + + constructor(private routeProvider, private pages, public profiles, currentProfile) { + for (let i = 0; i < this.profiles.length; ++i) { + if (this.profiles[i].id == currentProfile) { + this.profiles = this.profiles.slice(i); + break; + } + } + } + + public otherwise(options: any): void { + this.routeProvider.otherwise(options); + } + + public route(path: string, pagename: string): RouteBuilder { + // Lookup the page, matching our lists of profiles. + var pair = this.pages.get(pagename, this.profiles); + if (!pair) { + throw Error('Unknown page: ' + pagename); + } + + // Create the route. + var foundProfile = pair[0]; + var page = pair[1]; + var templateUrl = foundProfile.templatePath + page.templateName; + + var options = jQuery.extend({}, page.flags || {}); + options['templateUrl'] = templateUrl; + options['reloadOnSearch'] = false; + options['controller'] = page.controller; + + this.routeProvider.when(path, options); + + return this; + } +} \ No newline at end of file diff --git a/static/js/route-builder/route-builder.service.spec.ts b/static/js/route-builder/route-builder.service.spec.ts new file mode 100644 index 000000000..22bd51179 --- /dev/null +++ b/static/js/route-builder/route-builder.service.spec.ts @@ -0,0 +1,115 @@ +import { RouteBuilderImpl } from './route-builder.service.impl'; + + +describe("Service: RouteBuilderImpl", () => { + var routeProviderMock; + var pagesMock; + var profiles; + var currentProfile; + + it("sanity", () => { + expect(true).toBe(true); + }); + + beforeEach((() => { + profiles = [ + {id: 'old-layout', templatePath: '/static/partials/'}, + {id: 'layout', templatePath: '/static/partials/'} + ]; + currentProfile = 'layout'; + routeProviderMock = jasmine.createSpyObj('routeProvider', ['otherwise', 'when']); + pagesMock = jasmine.createSpyObj('pagesMock', ['get', 'create']); + })); + + describe("constructor", () => { + + it("returns a RouteBuilder object", () => { + var routeBuilder = new RouteBuilderImpl(routeProviderMock, pagesMock, profiles, currentProfile); + + expect(routeBuilder).toBeDefined(); + }); + + it("sets 'profiles' to all given profiles if given current profile does not match any of the given profiles' id", () => { + var routeBuilder = new RouteBuilderImpl(routeProviderMock, pagesMock, profiles, 'fake-profile'); + + expect(routeBuilder.profiles).toEqual(profiles); + }); + + it("sets 'profiles' to the first given profile with id matching given current profile", () => { + var routeBuilder = new RouteBuilderImpl(routeProviderMock, pagesMock, profiles, currentProfile); + + expect(routeBuilder.profiles).toEqual([profiles[1]]); + }); + }); + + describe("otherwise", () => { + var routeBuilder; + + beforeEach(() => { + routeBuilder = new RouteBuilderImpl(routeProviderMock, pagesMock, profiles, currentProfile); + }); + + it("calls routeProvider to set fallback route with given options", () => { + var options = {1: "option"}; + routeBuilder.otherwise(options); + + expect(routeProviderMock.otherwise.calls.argsFor(0)[0]).toEqual(options); + }); + }); + + describe("route", () => { + var routeBuilder; + var path; + var pagename; + var page; + + beforeEach(() => { + path = '/repository/:namespace/:name'; + pagename = 'repo-view'; + page = { + templateName: 'repository.html', + reloadOnSearch: false, + controller: jasmine.createSpy('pageController'), + flags: {}, + }; + routeBuilder = new RouteBuilderImpl(routeProviderMock, pagesMock, profiles, currentProfile); + }); + + it("calls pages with given pagename and 'profiles' to get matching page and profile pair", () => { + pagesMock.get.and.returnValue([profiles[1], page]); + routeBuilder.route(path, pagename); + + expect(pagesMock.get.calls.argsFor(0)[0]).toEqual(pagename); + expect(pagesMock.get.calls.argsFor(0)[1]).toEqual(routeBuilder.profiles); + }); + + it("throws error if no matching page/profile pair found", () => { + pagesMock.get.and.returnValue(); + try { + routeBuilder.route(path, pagename); + fail(); + } catch (error) { + expect(error.message).toEqual('Unknown page: ' + pagename); + } + }); + + it("calls routeProvider to set route for given path and options", () => { + pagesMock.get.and.returnValue([profiles[1], page]); + var expectedOptions = { + templateUrl: profiles[1].templatePath + page.templateName, + reloadOnSearch: false, + controller: page.controller, + }; + routeBuilder.route(path, pagename); + + expect(routeProviderMock.when.calls.argsFor(0)[0]).toEqual(path); + expect(routeProviderMock.when.calls.argsFor(0)[1]).toEqual(expectedOptions); + }); + + it("returns itself (the RouteBuilder instance)", () => { + pagesMock.get.and.returnValue([profiles[1], page]); + + expect(routeBuilder.route(path, pagename)).toEqual(routeBuilder); + }); + }); +}); \ No newline at end of file diff --git a/static/js/route-builder/route-builder.service.ts b/static/js/route-builder/route-builder.service.ts new file mode 100644 index 000000000..106f03ba1 --- /dev/null +++ b/static/js/route-builder/route-builder.service.ts @@ -0,0 +1,6 @@ +export abstract class RouteBuilder { + + public abstract otherwise(options: any): void; + + public abstract route(path: string, pagename: string): RouteBuilder; +} \ No newline at end of file diff --git a/static/js/route-builder/route-builder.spec.js b/static/js/route-builder/route-builder.spec.js deleted file mode 100644 index b8652316c..000000000 --- a/static/js/route-builder/route-builder.spec.js +++ /dev/null @@ -1,124 +0,0 @@ -describe("Service: RouteBuilder", function() { - var RouteBuilder; - var routeProviderMock; - var pagesMock; - var profiles; - var currentProfile; - - it("sanity", function() { - expect(true).toBe(true); - }); - - // beforeEach(module('quay')); - // - // beforeEach(inject(function($injector) { - // profiles = [ - // {id: 'old-layout', templatePath: '/static/partials/'}, - // {id: 'layout', templatePath: '/static/partials/'} - // ]; - // currentProfile = 'layout'; - // routeProviderMock = jasmine.createSpyObj('routeProvider', ['otherwise', 'when']); - // pagesMock = jasmine.createSpyObj('pagesMock', ['get', 'create']); - // RouteBuilder = $injector.get('RouteBuilder'); - // })); - // - // describe("constructor", function() { - // - // it("returns a RouteBuilder object", function() { - // var routeBuilder = new RouteBuilder(routeProviderMock, pagesMock, profiles, currentProfile); - // - // expect(routeBuilder).toBeDefined(); - // }); - // - // it("initializes dependencies", function() { - // var routeBuilder = new RouteBuilder(routeProviderMock, pagesMock, profiles, currentProfile); - // - // expect(routeBuilder.routeProvider).toEqual(routeProviderMock); - // expect(routeBuilder.pages).toEqual(pagesMock); - // expect(routeBuilder.profiles).toBeDefined(); - // }); - // - // it("sets 'profiles' to all given profiles if given current profile does not match any of the given profiles' id", function() { - // var routeBuilder = new RouteBuilder(routeProviderMock, pagesMock, profiles, 'fake-profile'); - // - // expect(routeBuilder.profiles).toEqual(profiles); - // }); - // - // it("sets 'profiles' to the first given profile with id matching given current profile", function() { - // var routeBuilder = new RouteBuilder(routeProviderMock, pagesMock, profiles, currentProfile); - // - // expect(routeBuilder.profiles).toEqual([profiles[1]]); - // }); - // }); - // - // describe("otherwise", function() { - // var routeBuilder; - // - // beforeEach(function() { - // routeBuilder = new RouteBuilder(routeProviderMock, pagesMock, profiles, currentProfile); - // }); - // - // it("calls routeProvider to set fallback route with given options", function() { - // var options = {1: "option"}; - // routeBuilder.otherwise(options); - // - // expect(routeProviderMock.otherwise.calls.argsFor(0)[0]).toEqual(options); - // }); - // }); - // - // describe("route", function() { - // var routeBuilder; - // var path; - // var pagename; - // var page; - // - // beforeEach(function() { - // path = '/repository/:namespace/:name'; - // pagename = 'repo-view'; - // page = { - // templateName: 'repository.html', - // reloadOnSearch: false, - // controller: jasmine.createSpy('pageController'), - // flags: {}, - // }; - // routeBuilder = new RouteBuilder(routeProviderMock, pagesMock, profiles, currentProfile); - // }); - // - // it("calls pages with given pagename and 'profiles' to get matching page and profile pair", function() { - // pagesMock.get.and.returnValue([profiles[1], page]); - // routeBuilder.route(path, pagename); - // - // expect(pagesMock.get.calls.argsFor(0)[0]).toEqual(pagename); - // expect(pagesMock.get.calls.argsFor(0)[1]).toEqual(routeBuilder.profiles); - // }); - // - // it("throws error if no matching page/profile pair found", function() { - // pagesMock.get.and.returnValue(); - // try { - // routeBuilder.route(path, pagename); - // fail(); - // } catch (error) { - // expect(error.message).toEqual('Unknown page: ' + pagename); - // } - // }); - // - // it("calls routeProvider to set route for given path and options", function() { - // pagesMock.get.and.returnValue([profiles[1], page]); - // var expectedOptions = { - // templateUrl: profiles[1].templatePath + page.templateName, - // reloadOnSearch: false, - // controller: page.controller, - // }; - // routeBuilder.route(path, pagename); - // - // expect(routeProviderMock.when.calls.argsFor(0)[0]).toEqual(path); - // expect(routeProviderMock.when.calls.argsFor(0)[1]).toEqual(expectedOptions); - // }); - // - // it("returns itself (the RouteBuilder instance)", function() { - // pagesMock.get.and.returnValue([profiles[1], page]); - // - // expect(routeBuilder.route(path, pagename)).toEqual(routeBuilder); - // }); - // }); -}); \ No newline at end of file