<commit_before>import fs from 'fs'

import { executeHooksWithArgs } from '@wdio/utils'
import { attach } from 'webdriverio'
import WDIORunner from '../src'
import logger from '@wdio/logger'

jest.mock('fs')
jest.mock('util', () => ({ promisify: (fn) => fn }))

describe('wdio-runner', () => {
    describe('_fetchDriverLogs', () => {
        let runner

        beforeEach(() => {
            runner = new WDIORunner()
            runner.cid = '0-1'
        })

        it('not do anything if driver does not support log commands', async () => {
            global.browser = { sessionId: '123' }

            const result = await runner._fetchDriverLogs({ outputDir: '/foo/bar' }, ['*'])
            expect(result).toBe(undefined)
        })

        it('should not write to file if all logs excluded', async () => {
            global.browser = {
                getLogTypes: () => Promise.resolve(['foo', 'bar']),
                getLogs: (type) => Promise.resolve([`#1 ${type} log`, `#2 ${type} log`]),
                sessionId: '123'
            }

            await runner._fetchDriverLogs({ outputDir: '/foo/bar' }, ['*'])

            expect(fs.writeFile).toHaveBeenCalledTimes(0)
        })

        it('should not write to file excluded logTypes', async () => {
            global.browser = {
                getLogTypes: () => Promise.resolve(['foo', 'bar']),
                getLogs: (type) => Promise.resolve([`#1 ${type} log`, `#2 ${type} log`]),
                sessionId: '123'
            }

            await runner._fetchDriverLogs({ outputDir: '/foo/bar' }, ['bar'])

            expect(fs.writeFile).toHaveBeenCalledTimes(1)

            expect(fs.writeFile.mock.calls[0][0]).toMatch(/(\\|\/)foo(\\|\/)bar(\\|\/)wdio-0-1-foo.log/)
            expect(fs.writeFile.mock.calls[0][1]).toEqual('"#1 foo log"\n"#2 foo log"')
            expect(fs.writeFile.mock.calls[0][2]).toEqual('utf-8')
        })

        it('should fetch logs', async () => {
            global.browser = {
                getLogTypes: () => Promise.resolve(['foo', 'bar']),
                getLogs: (type) => Promise.resolve([`#1 ${type} log`, `#2 ${type} log`]),
                sessionId: '123'
            }

            await runner._fetchDriverLogs({ outputDir: '/foo/bar' })
            expect(fs.writeFile.mock.calls[0][0]).toMatch(/(\\|\/)foo(\\|\/)bar(\\|\/)wdio-0-1-foo.log/)
            expect(fs.writeFile.mock.calls[0][1]).toEqual('"#1 foo log"\n"#2 foo log"')
            expect(fs.writeFile.mock.calls[0][2]).toEqual('utf-8')

            expect(fs.writeFile.mock.calls[1][0]).toMatch(/(\\|\/)foo(\\|\/)bar(\\|\/)wdio-0-1-bar.log/)
            expect(fs.writeFile.mock.calls[0][1]).toEqual('"#1 foo log"\n"#2 foo log"')
            expect(fs.writeFile.mock.calls[0][2]).toEqual('utf-8')

        })

        it('should not fail if logsTypes can not be received', async () => {
            global.browser = {
                getLogTypes: () => Promise.reject(new Error('boom')),
                getLogs: (type) => Promise.resolve([`#1 ${type} log`, `#2 ${type} log`]),
                sessionId: '123'
            }

            await runner._fetchDriverLogs({ outputDir: '/foo/bar' })
            expect(fs.writeFile).toHaveBeenCalledTimes(0)
        })

        it('should not fail if logs can not be received', async () => {
            global.browser = {
                getLogTypes: () => Promise.resolve(['corrupt']),
                getLogs: () => Promise.reject(new Error('boom')),
                sessionId: '123'
            }

            await runner._fetchDriverLogs({ outputDir: '/foo/bar' })
            expect(fs.writeFile).toHaveBeenCalledTimes(0)
        })

        it('should not write to file if no logs exist', async () => {
            global.browser = {
                getLogTypes: () => Promise.resolve(['foo', 'bar']),
                getLogs: () => Promise.resolve([]),
                sessionId: '123'
            }

            await runner._fetchDriverLogs({ outputDir: '/foo/bar' })
            expect(fs.writeFile.mock.calls).toHaveLength(0)
        })

        afterEach(() => {
            fs.writeFile.mockClear()
            delete global.browser
        })
    })

    describe('endSession', () => {
        it('should work normally when called after framework run', async () => {
            const hook = jest.fn()
            const runner = new WDIORunner()
            runner._shutdown = jest.fn()
            global.browser = {
                deleteSession: jest.fn(),
                sessionId: '123',
                config: { afterSession: [hook] }
            }
            await runner.endSession()
            expect(hook).toBeCalledTimes(1)
            expect(global.browser.deleteSession).toBeCalledTimes(1)
            expect(!global.browser.sessionId).toBe(true)
            expect(runner._shutdown).toBeCalledTimes(0)
        })

        it('should do nothing when triggered by run method without session', async () => {
            const hook = jest.fn()
            const runner = new WDIORunner()
            runner._shutdown = jest.fn()
            await runner.endSession()
            expect(hook).toBeCalledTimes(0)
        })

        it('should work normally when called after framework run in multiremote', async () => {
            const hook = jest.fn()
            const runner = new WDIORunner()
            runner.isMultiremote = true
            runner._shutdown = jest.fn()
            global.browser = {
                deleteSession: jest.fn(),
                instances: ['foo', 'bar'],
                foo: {
                    sessionId: '123',
                },
                bar: {
                    sessionId: '456',
                },
                config: { afterSession: [hook] }
            }
            await runner.endSession()
            expect(hook).toBeCalledTimes(1)
            expect(global.browser.deleteSession).toBeCalledTimes(1)
            expect(!global.browser.foo.sessionId).toBe(true)
            expect(!global.browser.bar.sessionId).toBe(true)
            expect(runner._shutdown).toBeCalledTimes(0)
        })

        it('should do nothing when triggered by run method without session in multiremote', async () => {
            const hook = jest.fn()
            const runner = new WDIORunner()
            runner.isMultiremote = true
            runner._shutdown = jest.fn()
            await runner.endSession()
            expect(hook).toBeCalledTimes(0)
        })

        afterEach(() => {
            delete global.browser
        })
    })

    describe('run', () => {
        it('should fail if log file is corrupted', async () => {
            const runner = new WDIORunner()
            runner._shutdown = jest.fn()
            runner.configParser.addConfigFile = jest.fn().mockImplementation(
                () => { throw new Error('boom') })
            await runner.run({})

            expect(runner._shutdown).toBeCalledWith(1)
        })

        it('should fail if init session fails', async () => {
            const runner = new WDIORunner()
            const beforeSession = jest.fn()
            const before = jest.fn()
            const caps = { browserName: '123' }
            const specs = ['foobar']
            const config = {
                reporters: [],
                before: [before],
                beforeSession: [beforeSession],
                framework: 'testWithFailures'
            }
            runner.configParser.getConfig = jest.fn().mockReturnValue(config)
            runner._shutdown = jest.fn()
            runner._initSession = jest.fn().mockReturnValue({
                capabilities: { browserName: 'chrome' },
                options: {}
            })
            await runner.run({
                args: { reporters: [] },
                cid: '0-0',
                caps,
                specs
            })

            expect(runner._shutdown).toBeCalledWith(123)
            expect(beforeSession).toBeCalledWith(config, caps, specs)
            expect(executeHooksWithArgs).toBeCalledWith(config.before, [caps, specs])

            // session capabilities should be passed to reporter
            expect(runner.reporter.caps).toEqual({ browserName: 'chrome' })
        })

        it('should return failures count', async () => {
            const runner = new WDIORunner()
            const config = {
                framework: 'testNoFailures',
                reporters: [],
                beforeSession: []
            }
            runner.configParser.getConfig = jest.fn().mockReturnValue(config)
            runner._initSession = jest.fn().mockReturnValue({ options: { capabilities: {} } })
            const failures = await runner.run({ args: {}, caps: {} })

            expect(failures).toBe(0)
        })

        it('should not call browser url if args watch', async () => {
            const runner = new WDIORunner()
            const config = {
                framework: 'testNoFailures',
                reporters: [],
                beforeSession: []
            }
            runner.configParser.getConfig = jest.fn().mockReturnValue(config)
            global.browser = { url: jest.fn(url => url) }
            runner._startSession = jest.fn().mockReturnValue({ })
            runner._initSession = jest.fn().mockReturnValue({ options: { capabilities: {} } })
            const failures = await runner.run({ args: { watch: true }, caps: {} })

            expect(failures).toBe(0)
            expect(global.browser.url).not.toBeCalled()
        })

        it('should set failures to 1 in case of error', async () => {
            const runner = new WDIORunner()
            const config = {
                framework: 'testThrows',
                reporters: [],
                beforeSession: []
            }
            runner.configParser.getConfig = jest.fn().mockReturnValue(config)
            runner._initSession = jest.fn().mockReturnValue({ options: { capabilities: {} } })
            runner.emit = jest.fn()
            const failures = await runner.run({ args: {}, caps: {} })

            expect(failures).toBe(1)
            expect(runner.emit.mock.calls[0]).toEqual(['error', new Error('framework testThrows failed')])
        })

        it('should return if sigintWasCalled', async () => {
            const runner = new WDIORunner()
            const caps = { browserName: '123' }
            const specs = ['foobar']
            const config = {
                framework: 'testThrows',
                reporters: [],
                beforeSession: []
            }
            runner.configParser.getConfig = jest.fn().mockReturnValue(config)
            runner._shutdown = jest.fn()
            runner.endSession = jest.fn()
            runner._initSession = jest.fn().mockReturnValue({})
            runner.sigintWasCalled = true
            await runner.run({
                args: { reporters: [] },
                cid: '0-0',
                caps,
                specs
            })

            expect(runner.endSession).toBeCalledTimes(1)
            expect(runner._shutdown).toBeCalledWith(0)
        })

        it('should not initSession if there are no tests to run', async () => {
            const runner = new WDIORunner()
            const config = {
                framework: 'testNoTests',
                reporters: [],
                beforeSession: []
            }
            runner.configParser.getConfig = jest.fn().mockReturnValue(config)
            runner._shutdown = jest.fn().mockImplementation((arg) => arg)
            runner._initSession = jest.fn()

            expect(await runner.run({ args: {}, caps: {} })).toBe(0)
            expect(runner._shutdown).toBeCalledWith(0)
            expect(runner._initSession).not.toBeCalled()
        })

        it('should shutdown if session was not created', async () => {
            const runner = new WDIORunner()
            const caps = { browserName: '123' }
            const specs = ['foobar']
            const config = {
                framework: 'testNoFailures',
                reporters: [],
                beforeSession: []
            }
            runner.configParser.getConfig = jest.fn().mockReturnValue(config)
            runner._shutdown = jest.fn().mockReturnValue('_shutdown')
            runner.endSession = jest.fn()
            runner._initSession = jest.fn().mockReturnValue(null)
            expect(await runner.run({
                args: { reporters: [] },
                cid: '0-0',
                caps,
                specs
            })).toBe('_shutdown')

            expect(runner._shutdown).toBeCalledWith(1)

            // user defined capabilities should be used until
            // browser session is started
            expect(runner.reporter.caps).toEqual(caps)
        })

        afterEach(() => {
            delete global.browser
        })
    })

    describe('_initSession', () => {
        it('should register browser to global scope', async () => {
            const runner = new WDIORunner()
            const browser = await runner._initSession(
                { hostname: 'foobar' },
                [{ browserName: 'chrome1' }]
            )

            expect(browser).toBe(global.browser)
            expect(typeof $).toBe('function')
            expect(typeof $$).toBe('function')

            expect(browser.$).toBeCalledTimes(0)
            expect(browser.$$).toBeCalledTimes(0)
            /* eslint-disable-next-line */
            $('foobar')
            /* eslint-disable-next-line */
            $$('barfoo')
            expect(browser.$).toBeCalledTimes(1)
            expect(browser.$$).toBeCalledTimes(1)
        })

        it('should register before and after command listener', async () => {
            const reporter = { emit: jest.fn() }
            const runner = new WDIORunner()

            runner.reporter = reporter
            const browser = await runner._initSession(
                { hostname: 'foobar' },
                [{ browserName: 'chrome' }]
            )

            const beforeListener = browser.on.mock.calls[0]
            expect(beforeListener[0]).toBe('command')
            beforeListener[1]({ foo: 'bar' })
            expect(reporter.emit).toBeCalledWith(
                'client:beforeCommand',
                { foo: 'bar', sessionId: 'fakeid' })

            reporter.emit.mockClear()

            const afterListener = browser.on.mock.calls[1]
            expect(afterListener[0]).toBe('result')
            afterListener[1]({ bar: 'foo' })
            expect(reporter.emit).toBeCalledWith(
                'client:afterCommand',
                { bar: 'foo', sessionId: 'fakeid' })
        })

        it('should return null if initiating session fails', async () => {
            global.throwRemoteCall = true
            const runner = new WDIORunner()
            runner.emit = jest.fn()
            const browser = await runner._initSession(
                { hostname: 'foobar' },
                [{ browserName: 'chrome' }]
            )

            expect(browser).toBe(null)
            expect(runner.emit).toBeCalledWith('error', new Error('boom'))
        })

        afterEach(() => {
            delete global.throwRemoteCall
            delete global.browser
            delete global.driver
            delete global.$
            delete global.$$
        })
    })

    describe('_shutdown', () => {
        it('should emit exit', async () => {
            const runner = new WDIORunner()
            runner.reporter = { waitForSync: jest.fn()
                .mockReturnValue(Promise.resolve()) }
            runner.emit = jest.fn()

            expect(await runner._shutdown(123)).toBe(123)
            expect(runner.reporter.waitForSync).toBeCalledTimes(1)
            expect(runner.emit).toBeCalledWith('exit', 1)
        })

        it('should emit exit when reporter unsync', async () => {
            const log = logger('wdio-runner')
            jest.spyOn(log, 'error').mockImplementation((string) => string)

            const runner = new WDIORunner()
            runner.reporter = { waitForSync: jest.fn().mockReturnValue(Promise.reject('foo')) }
            runner.emit = jest.fn()

            expect(await runner._shutdown(123)).toBe(123)
            expect(runner.reporter.waitForSync).toBeCalledTimes(1)
            expect(runner.emit).toBeCalledWith('exit', 1)
            expect(log.error).toHaveBeenCalledWith('foo')
        })
    })

    afterEach(() => {
        executeHooksWithArgs.mockClear()
        attach.mockClear()
        delete global.browser
    })
})
<commit_msg>fix unit test<commit_after>import fs from 'fs'
import { promisify } from 'util'

import { executeHooksWithArgs } from '@wdio/utils'
import { attach } from 'webdriverio'
import WDIORunner from '../src'
import logger from '@wdio/logger'

jest.mock('fs')
jest.mock('util')

promisify.mockImplementation((fn) => fn)

describe('wdio-runner', () => {
    describe('_fetchDriverLogs', () => {
        let runner

        beforeEach(() => {
            runner = new WDIORunner()
            runner.cid = '0-1'
        })

        it('not do anything if driver does not support log commands', async () => {
            global.browser = { sessionId: '123' }

            const result = await runner._fetchDriverLogs({ outputDir: '/foo/bar' }, ['*'])
            expect(result).toBe(undefined)
        })

        it('should not write to file if all logs excluded', async () => {
            global.browser = {
                getLogTypes: () => Promise.resolve(['foo', 'bar']),
                getLogs: (type) => Promise.resolve([`#1 ${type} log`, `#2 ${type} log`]),
                sessionId: '123'
            }

            await runner._fetchDriverLogs({ outputDir: '/foo/bar' }, ['*'])

            expect(fs.writeFile).toHaveBeenCalledTimes(0)
        })

        it('should not write to file excluded logTypes', async () => {
            global.browser = {
                getLogTypes: () => Promise.resolve(['foo', 'bar']),
                getLogs: (type) => Promise.resolve([`#1 ${type} log`, `#2 ${type} log`]),
                sessionId: '123'
            }

            await runner._fetchDriverLogs({ outputDir: '/foo/bar' }, ['bar'])

            expect(fs.writeFile).toHaveBeenCalledTimes(1)

            expect(fs.writeFile.mock.calls[0][0]).toMatch(/(\\|\/)foo(\\|\/)bar(\\|\/)wdio-0-1-foo.log/)
            expect(fs.writeFile.mock.calls[0][1]).toEqual('"#1 foo log"\n"#2 foo log"')
            expect(fs.writeFile.mock.calls[0][2]).toEqual('utf-8')
        })

        it('should fetch logs', async () => {
            global.browser = {
                getLogTypes: () => Promise.resolve(['foo', 'bar']),
                getLogs: (type) => Promise.resolve([`#1 ${type} log`, `#2 ${type} log`]),
                sessionId: '123'
            }

            await runner._fetchDriverLogs({ outputDir: '/foo/bar' })
            expect(fs.writeFile.mock.calls[0][0]).toMatch(/(\\|\/)foo(\\|\/)bar(\\|\/)wdio-0-1-foo.log/)
            expect(fs.writeFile.mock.calls[0][1]).toEqual('"#1 foo log"\n"#2 foo log"')
            expect(fs.writeFile.mock.calls[0][2]).toEqual('utf-8')

            expect(fs.writeFile.mock.calls[1][0]).toMatch(/(\\|\/)foo(\\|\/)bar(\\|\/)wdio-0-1-bar.log/)
            expect(fs.writeFile.mock.calls[0][1]).toEqual('"#1 foo log"\n"#2 foo log"')
            expect(fs.writeFile.mock.calls[0][2]).toEqual('utf-8')

        })

        it('should not fail if logsTypes can not be received', async () => {
            global.browser = {
                getLogTypes: () => Promise.reject(new Error('boom')),
                getLogs: (type) => Promise.resolve([`#1 ${type} log`, `#2 ${type} log`]),
                sessionId: '123'
            }

            await runner._fetchDriverLogs({ outputDir: '/foo/bar' })
            expect(fs.writeFile).toHaveBeenCalledTimes(0)
        })

        it('should not fail if logs can not be received', async () => {
            global.browser = {
                getLogTypes: () => Promise.resolve(['corrupt']),
                getLogs: () => Promise.reject(new Error('boom')),
                sessionId: '123'
            }

            await runner._fetchDriverLogs({ outputDir: '/foo/bar' })
            expect(fs.writeFile).toHaveBeenCalledTimes(0)
        })

        it('should not write to file if no logs exist', async () => {
            global.browser = {
                getLogTypes: () => Promise.resolve(['foo', 'bar']),
                getLogs: () => Promise.resolve([]),
                sessionId: '123'
            }

            await runner._fetchDriverLogs({ outputDir: '/foo/bar' })
            expect(fs.writeFile.mock.calls).toHaveLength(0)
        })

        afterEach(() => {
            fs.writeFile.mockClear()
            delete global.browser
        })
    })

    describe('endSession', () => {
        it('should work normally when called after framework run', async () => {
            const hook = jest.fn()
            const runner = new WDIORunner()
            runner._shutdown = jest.fn()
            global.browser = {
                deleteSession: jest.fn(),
                sessionId: '123',
                config: { afterSession: [hook] }
            }
            await runner.endSession()
            expect(hook).toBeCalledTimes(1)
            expect(global.browser.deleteSession).toBeCalledTimes(1)
            expect(!global.browser.sessionId).toBe(true)
            expect(runner._shutdown).toBeCalledTimes(0)
        })

        it('should do nothing when triggered by run method without session', async () => {
            const hook = jest.fn()
            const runner = new WDIORunner()
            runner._shutdown = jest.fn()
            await runner.endSession()
            expect(hook).toBeCalledTimes(0)
        })

        it('should work normally when called after framework run in multiremote', async () => {
            const hook = jest.fn()
            const runner = new WDIORunner()
            runner.isMultiremote = true
            runner._shutdown = jest.fn()
            global.browser = {
                deleteSession: jest.fn(),
                instances: ['foo', 'bar'],
                foo: {
                    sessionId: '123',
                },
                bar: {
                    sessionId: '456',
                },
                config: { afterSession: [hook] }
            }
            await runner.endSession()
            expect(hook).toBeCalledTimes(1)
            expect(global.browser.deleteSession).toBeCalledTimes(1)
            expect(!global.browser.foo.sessionId).toBe(true)
            expect(!global.browser.bar.sessionId).toBe(true)
            expect(runner._shutdown).toBeCalledTimes(0)
        })

        it('should do nothing when triggered by run method without session in multiremote', async () => {
            const hook = jest.fn()
            const runner = new WDIORunner()
            runner.isMultiremote = true
            runner._shutdown = jest.fn()
            await runner.endSession()
            expect(hook).toBeCalledTimes(0)
        })

        afterEach(() => {
            delete global.browser
        })
    })

    describe('run', () => {
        it('should fail if log file is corrupted', async () => {
            const runner = new WDIORunner()
            runner._shutdown = jest.fn()
            runner.configParser.addConfigFile = jest.fn().mockImplementation(
                () => { throw new Error('boom') })
            await runner.run({})

            expect(runner._shutdown).toBeCalledWith(1)
        })

        it('should fail if init session fails', async () => {
            const runner = new WDIORunner()
            const beforeSession = jest.fn()
            const before = jest.fn()
            const caps = { browserName: '123' }
            const specs = ['foobar']
            const config = {
                reporters: [],
                before: [before],
                beforeSession: [beforeSession],
                framework: 'testWithFailures'
            }
            runner.configParser.getConfig = jest.fn().mockReturnValue(config)
            runner._shutdown = jest.fn()
            runner._initSession = jest.fn().mockReturnValue({
                capabilities: { browserName: 'chrome' },
                options: {}
            })
            await runner.run({
                args: { reporters: [] },
                cid: '0-0',
                caps,
                specs
            })

            expect(runner._shutdown).toBeCalledWith(123)
            expect(beforeSession).toBeCalledWith(config, caps, specs)
            expect(executeHooksWithArgs).toBeCalledWith(config.before, [caps, specs])

            // session capabilities should be passed to reporter
            expect(runner.reporter.caps).toEqual({ browserName: 'chrome' })
        })

        it('should return failures count', async () => {
            const runner = new WDIORunner()
            const config = {
                framework: 'testNoFailures',
                reporters: [],
                beforeSession: []
            }
            runner.configParser.getConfig = jest.fn().mockReturnValue(config)
            runner._initSession = jest.fn().mockReturnValue({ options: { capabilities: {} } })
            const failures = await runner.run({ args: {}, caps: {} })

            expect(failures).toBe(0)
        })

        it('should not call browser url if args watch', async () => {
            const runner = new WDIORunner()
            const config = {
                framework: 'testNoFailures',
                reporters: [],
                beforeSession: []
            }
            runner.configParser.getConfig = jest.fn().mockReturnValue(config)
            global.browser = { url: jest.fn(url => url) }
            runner._startSession = jest.fn().mockReturnValue({ })
            runner._initSession = jest.fn().mockReturnValue({ options: { capabilities: {} } })
            const failures = await runner.run({ args: { watch: true }, caps: {} })

            expect(failures).toBe(0)
            expect(global.browser.url).not.toBeCalled()
        })

        it('should set failures to 1 in case of error', async () => {
            const runner = new WDIORunner()
            const config = {
                framework: 'testThrows',
                reporters: [],
                beforeSession: []
            }
            runner.configParser.getConfig = jest.fn().mockReturnValue(config)
            runner._initSession = jest.fn().mockReturnValue({ options: { capabilities: {} } })
            runner.emit = jest.fn()
            const failures = await runner.run({ args: {}, caps: {} })

            expect(failures).toBe(1)
            expect(runner.emit.mock.calls[0]).toEqual(['error', new Error('framework testThrows failed')])
        })

        it('should return if sigintWasCalled', async () => {
            const runner = new WDIORunner()
            const caps = { browserName: '123' }
            const specs = ['foobar']
            const config = {
                framework: 'testThrows',
                reporters: [],
                beforeSession: []
            }
            runner.configParser.getConfig = jest.fn().mockReturnValue(config)
            runner._shutdown = jest.fn()
            runner.endSession = jest.fn()
            runner._initSession = jest.fn().mockReturnValue({})
            runner.sigintWasCalled = true
            await runner.run({
                args: { reporters: [] },
                cid: '0-0',
                caps,
                specs
            })

            expect(runner.endSession).toBeCalledTimes(1)
            expect(runner._shutdown).toBeCalledWith(0)
        })

        it('should not initSession if there are no tests to run', async () => {
            const runner = new WDIORunner()
            const config = {
                framework: 'testNoTests',
                reporters: [],
                beforeSession: []
            }
            runner.configParser.getConfig = jest.fn().mockReturnValue(config)
            runner._shutdown = jest.fn().mockImplementation((arg) => arg)
            runner._initSession = jest.fn()

            expect(await runner.run({ args: {}, caps: {} })).toBe(0)
            expect(runner._shutdown).toBeCalledWith(0)
            expect(runner._initSession).not.toBeCalled()
        })

        it('should shutdown if session was not created', async () => {
            const runner = new WDIORunner()
            const caps = { browserName: '123' }
            const specs = ['foobar']
            const config = {
                framework: 'testNoFailures',
                reporters: [],
                beforeSession: []
            }
            runner.configParser.getConfig = jest.fn().mockReturnValue(config)
            runner._shutdown = jest.fn().mockReturnValue('_shutdown')
            runner.endSession = jest.fn()
            runner._initSession = jest.fn().mockReturnValue(null)
            expect(await runner.run({
                args: { reporters: [] },
                cid: '0-0',
                caps,
                specs
            })).toBe('_shutdown')

            expect(runner._shutdown).toBeCalledWith(1)

            // user defined capabilities should be used until
            // browser session is started
            expect(runner.reporter.caps).toEqual(caps)
        })

        afterEach(() => {
            delete global.browser
        })
    })

    describe('_initSession', () => {
        it('should register browser to global scope', async () => {
            const runner = new WDIORunner()
            const browser = await runner._initSession(
                { hostname: 'foobar' },
                [{ browserName: 'chrome1' }]
            )

            expect(browser).toBe(global.browser)
            expect(typeof $).toBe('function')
            expect(typeof $$).toBe('function')

            expect(browser.$).toBeCalledTimes(0)
            expect(browser.$$).toBeCalledTimes(0)
            /* eslint-disable-next-line */
            $('foobar')
            /* eslint-disable-next-line */
            $$('barfoo')
            expect(browser.$).toBeCalledTimes(1)
            expect(browser.$$).toBeCalledTimes(1)
        })

        it('should register before and after command listener', async () => {
            const reporter = { emit: jest.fn() }
            const runner = new WDIORunner()

            runner.reporter = reporter
            const browser = await runner._initSession(
                { hostname: 'foobar' },
                [{ browserName: 'chrome' }]
            )

            const beforeListener = browser.on.mock.calls[0]
            expect(beforeListener[0]).toBe('command')
            beforeListener[1]({ foo: 'bar' })
            expect(reporter.emit).toBeCalledWith(
                'client:beforeCommand',
                { foo: 'bar', sessionId: 'fakeid' })

            reporter.emit.mockClear()

            const afterListener = browser.on.mock.calls[1]
            expect(afterListener[0]).toBe('result')
            afterListener[1]({ bar: 'foo' })
            expect(reporter.emit).toBeCalledWith(
                'client:afterCommand',
                { bar: 'foo', sessionId: 'fakeid' })
        })

        it('should return null if initiating session fails', async () => {
            global.throwRemoteCall = true
            const runner = new WDIORunner()
            runner.emit = jest.fn()
            const browser = await runner._initSession(
                { hostname: 'foobar' },
                [{ browserName: 'chrome' }]
            )

            expect(browser).toBe(null)
            expect(runner.emit).toBeCalledWith('error', new Error('boom'))
        })

        afterEach(() => {
            delete global.throwRemoteCall
            delete global.browser
            delete global.driver
            delete global.$
            delete global.$$
        })
    })

    describe('_shutdown', () => {
        it('should emit exit', async () => {
            const runner = new WDIORunner()
            runner.reporter = { waitForSync: jest.fn()
                .mockReturnValue(Promise.resolve()) }
            runner.emit = jest.fn()

            expect(await runner._shutdown(123)).toBe(123)
            expect(runner.reporter.waitForSync).toBeCalledTimes(1)
            expect(runner.emit).toBeCalledWith('exit', 1)
        })

        it('should emit exit when reporter unsync', async () => {
            const log = logger('wdio-runner')
            jest.spyOn(log, 'error').mockImplementation((string) => string)

            const runner = new WDIORunner()
            runner.reporter = { waitForSync: jest.fn().mockReturnValue(Promise.reject('foo')) }
            runner.emit = jest.fn()

            expect(await runner._shutdown(123)).toBe(123)
            expect(runner.reporter.waitForSync).toBeCalledTimes(1)
            expect(runner.emit).toBeCalledWith('exit', 1)
            expect(log.error).toHaveBeenCalledWith('foo')
        })
    })

    afterEach(() => {
        executeHooksWithArgs.mockClear()
        attach.mockClear()
        delete global.browser
    })
})
<|endoftext|>


<commit_before>import R from 'ramda';
import thunk from 'redux-thunk';
import { createStore, applyMiddleware } from 'redux';
import { assert } from 'chai';
import { Maybe } from 'ramda-fantasy';

import {
  getProjectName,
  lensPatch,
  listLocalPatches,
  listLibraryPatches,
  getPatchByPath,
  getPatchPath,
  getBaseName,
  getNodeById,
  getNodePosition,
  getNodeLabel,
  getPinCurriedValue,
  listLinks,
  getLinkId,
} from 'xod-project';
import initialState from '../src/core/state';
import generateReducers from '../src/core/reducer';
import { getProject } from '../src/project/selectors';
import {
  createProject,
  renameProject,
  addPatch,
  renamePatch,
  deletePatch,
  addNode,
  moveNode,
  updateNodeProperty,
  deleteNode,
  addLink,
  deleteLink,
} from '../src/project/actions';

describe('project reducer', () => {
  describe('Project management', () => {
    let store = null;

    beforeEach(
      () => {
        store = createStore(generateReducers(), initialState, applyMiddleware(thunk));
      }
    );

    it('should create a project', () => {
      const newProjectName = 'new-test-project';
      const initialProject = getProject(store.getState());
      store.dispatch(createProject(newProjectName));

      const newProject = getProject(store.getState());
      assert.notEqual(initialProject, newProject);
      assert.equal(
        newProjectName,
        getProjectName(newProject)
      );
      assert.deepEqual(
        [
          {
            impls: {},
            links: {},
            nodes: {},
            path: '@/main',
          },
        ],
        listLocalPatches(newProject),
        'new project has an empty patch with a name "main"'
      );
      assert.deepEqual(
        listLibraryPatches(initialProject),
        listLibraryPatches(newProject),
        'new project has the same library patches'
      );
    });

    it('should rename a project', () => {
      const initialProject = getProject(store.getState());
      const newName = 'new-name-for-my-project';
      store.dispatch(renameProject(newName));

      const renamedProject = getProject(store.getState());

      assert.equal(
        newName,
        getProjectName(renamedProject)
      );
      assert.deepEqual( // isTrue(R.eqBy(...)) will not provide a nice diff
        R.omit(['name'], initialProject),
        R.omit(['name'], renamedProject),
        'everything except the name stays the same'
      );
    });
  });

  describe('Patch management', () => {
    let store = null;

    beforeEach(
      () => {
        store = createStore(generateReducers(), initialState, applyMiddleware(thunk));
      }
    );

    it('should add a patch', () => {
      const addPatchAction = store.dispatch(addPatch('test-patch'));
      const newPatchPath = addPatchAction.payload.patchPath;

      const project = getProject(store.getState());
      const maybeNewPatch = getPatchByPath(newPatchPath, project);
      assert.isTrue(Maybe.isJust(maybeNewPatch));
    });

    it('should rename a patch', () => {
      const addPatchAction = store.dispatch(addPatch('initial-name'));
      const initialPatchPath = addPatchAction.payload.patchPath;
      const newPatchName = 'new-patch-name';
      const renameAction = store.dispatch(renamePatch(initialPatchPath, newPatchName));
      const { newPatchPath } = renameAction.payload;

      const project = getProject(store.getState());
      const renamedPatch = getPatchByPath(newPatchPath, project).getOrElse(null);

      assert.isOk(renamedPatch);
      assert.equal(
        R.compose(getBaseName, getPatchPath)(renamedPatch),
        newPatchName
      );
    });

    it('should delete a patch', () => {
      const addPatchAction = store.dispatch(addPatch('label'));
      const patchPath = addPatchAction.payload.patchPath;
      store.dispatch(deletePatch(patchPath));

      const project = getProject(store.getState());
      const maybeDeletedPatch = getPatchByPath(patchPath, project);

      assert.isTrue(Maybe.isNothing(maybeDeletedPatch));
    });
  });

  describe('Node management', () => {
    let store = null;
    let testPatchPath = '';

    beforeEach(
      () => {
        store = createStore(generateReducers(), initialState, applyMiddleware(thunk));
        const addPatchAction = store.dispatch(addPatch('test-patch'));
        testPatchPath = addPatchAction.payload.patchPath;
      }
    );

    it('should add a node', () => {
      const nodeId = store.dispatch(addNode('xod/patch-nodes/input-number', { x: 0, y: 0 }, testPatchPath));

      const maybeNode = R.compose(
        getNodeById(nodeId),
        R.view(lensPatch(testPatchPath)),
        getProject
      )(store.getState());

      assert.isTrue(Maybe.isJust(maybeNode));
    });
    it('should move a node', () => {
      const nodeId = store.dispatch(addNode('xod/patch-nodes/input-number', { x: 0, y: 0 }, testPatchPath));
      const desiredPosition = { x: 111, y: 222 };
      store.dispatch(moveNode(nodeId, desiredPosition));

      const maybeNode = R.compose(
        getNodeById(nodeId),
        R.view(lensPatch(testPatchPath)),
        getProject
      )(store.getState());

      const actualPosition = Maybe.maybe({}, getNodePosition, maybeNode);

      assert.deepEqual(
        desiredPosition,
        actualPosition
      );
    });
    it('should update node label', () => {
      const nodeId = store.dispatch(addNode('xod/patch-nodes/input-number', { x: 0, y: 0 }, testPatchPath));
      const desiredLabel = 'desired label';
      store.dispatch(updateNodeProperty(nodeId, 'property', 'label', desiredLabel));

      const maybeNode = R.compose(
        getNodeById(nodeId),
        R.view(lensPatch(testPatchPath)),
        getProject
      )(store.getState());

      const actualLabel = Maybe.maybe({}, getNodeLabel, maybeNode);

      assert.deepEqual(
        desiredLabel,
        actualLabel
      );
    });
    it('should update node\'s pin value ', () => {
      const nodeId = store.dispatch(addNode('xod/patch-nodes/output-number', { x: 0, y: 0 }, testPatchPath));
      const pinKey = '__in__';
      const desiredPinValue = 42;
      store.dispatch(updateNodeProperty(nodeId, 'pin', pinKey, desiredPinValue));

      const maybeNode = R.compose(
        getNodeById(nodeId),
        R.view(lensPatch(testPatchPath)),
        getProject
      )(store.getState());

      const maybePinValue = maybeNode.chain(getPinCurriedValue(pinKey));

      const actualPinValue = Maybe.maybe({}, R.identity, maybePinValue);

      assert.deepEqual(
        desiredPinValue,
        actualPinValue
      );
    });
    it('should delete a node', () => {
      const nodeId = store.dispatch(addNode('xod/patch-nodes/input-number', { x: 0, y: 0 }, testPatchPath));
      store.dispatch(deleteNode(nodeId));

      const maybeNode = R.compose(
        getNodeById(nodeId),
        R.view(lensPatch(testPatchPath)),
        getProject
      )(store.getState());

      assert.isTrue(Maybe.isNothing(maybeNode));
    });
  });

  describe('Link management', () => {
    let store = null;
    let testPatchPath = '';
    let inNodeId = '';
    let outNodeId = '';

    beforeEach(
      () => {
        store = createStore(generateReducers(), initialState, applyMiddleware(thunk));
        const addPatchAction = store.dispatch(addPatch('test-patch'));
        testPatchPath = addPatchAction.payload.patchPath;
        inNodeId = store.dispatch(addNode('xod/patch-nodes/input-number', { x: 100, y: 100 }, testPatchPath));
        outNodeId = store.dispatch(addNode('xod/patch-nodes/output-number', { x: 500, y: 500 }, testPatchPath));
      }
    );

    it('should add a link', () => {
      store.dispatch(addLink(
        { nodeId: inNodeId, pinKey: '__out__' },
        { nodeId: outNodeId, pinKey: '__in__' }
      ));

      const links = R.compose(
        listLinks,
        R.view(lensPatch(testPatchPath)),
        getProject
      )(store.getState());

      assert.equal(1, links.length);
    });

    it('should delete a link', () => {
      store.dispatch(addLink(
        { nodeId: inNodeId, pinKey: '__out__' },
        { nodeId: outNodeId, pinKey: '__in__' }
      ));

      const linkId = R.compose(
        getLinkId,
        R.head,
        listLinks,
        R.view(lensPatch(testPatchPath)),
        getProject
      )(store.getState());

      store.dispatch(deleteLink(linkId, testPatchPath));

      const links = R.compose(
        listLinks,
        R.view(lensPatch(testPatchPath)),
        getProject
      )(store.getState());

      assert.equal(0, links.length);
    });
  });
});

<commit_msg>chore(xod-client): tweak test fixture<commit_after>import R from 'ramda';
import thunk from 'redux-thunk';
import { createStore, applyMiddleware } from 'redux';
import { assert } from 'chai';
import { Maybe } from 'ramda-fantasy';

import {
  getProjectName,
  lensPatch,
  listLocalPatches,
  listLibraryPatches,
  getPatchByPath,
  getPatchPath,
  getBaseName,
  getNodeById,
  getNodePosition,
  getNodeLabel,
  getPinCurriedValue,
  listLinks,
  getLinkId,
} from 'xod-project';
import initialState from '../src/core/state';
import generateReducers from '../src/core/reducer';
import { getProject } from '../src/project/selectors';
import {
  createProject,
  renameProject,
  addPatch,
  renamePatch,
  deletePatch,
  addNode,
  moveNode,
  updateNodeProperty,
  deleteNode,
  addLink,
  deleteLink,
} from '../src/project/actions';

describe('project reducer', () => {
  describe('Project management', () => {
    let store = null;

    beforeEach(
      () => {
        store = createStore(generateReducers(), initialState, applyMiddleware(thunk));
      }
    );

    it('should create a project', () => {
      const newProjectName = 'new-test-project';
      const initialProject = getProject(store.getState());
      store.dispatch(createProject(newProjectName));

      const newProject = getProject(store.getState());
      assert.notEqual(initialProject, newProject);
      assert.equal(
        newProjectName,
        getProjectName(newProject)
      );
      assert.deepEqual(
        [
          {
            impls: {},
            links: {},
            nodes: {},
            path: '@/main',
            description: '',
          },
        ],
        listLocalPatches(newProject),
        'new project has an empty patch with a name "main"'
      );
      assert.deepEqual(
        listLibraryPatches(initialProject),
        listLibraryPatches(newProject),
        'new project has the same library patches'
      );
    });

    it('should rename a project', () => {
      const initialProject = getProject(store.getState());
      const newName = 'new-name-for-my-project';
      store.dispatch(renameProject(newName));

      const renamedProject = getProject(store.getState());

      assert.equal(
        newName,
        getProjectName(renamedProject)
      );
      assert.deepEqual( // isTrue(R.eqBy(...)) will not provide a nice diff
        R.omit(['name'], initialProject),
        R.omit(['name'], renamedProject),
        'everything except the name stays the same'
      );
    });
  });

  describe('Patch management', () => {
    let store = null;

    beforeEach(
      () => {
        store = createStore(generateReducers(), initialState, applyMiddleware(thunk));
      }
    );

    it('should add a patch', () => {
      const addPatchAction = store.dispatch(addPatch('test-patch'));
      const newPatchPath = addPatchAction.payload.patchPath;

      const project = getProject(store.getState());
      const maybeNewPatch = getPatchByPath(newPatchPath, project);
      assert.isTrue(Maybe.isJust(maybeNewPatch));
    });

    it('should rename a patch', () => {
      const addPatchAction = store.dispatch(addPatch('initial-name'));
      const initialPatchPath = addPatchAction.payload.patchPath;
      const newPatchName = 'new-patch-name';
      const renameAction = store.dispatch(renamePatch(initialPatchPath, newPatchName));
      const { newPatchPath } = renameAction.payload;

      const project = getProject(store.getState());
      const renamedPatch = getPatchByPath(newPatchPath, project).getOrElse(null);

      assert.isOk(renamedPatch);
      assert.equal(
        R.compose(getBaseName, getPatchPath)(renamedPatch),
        newPatchName
      );
    });

    it('should delete a patch', () => {
      const addPatchAction = store.dispatch(addPatch('label'));
      const patchPath = addPatchAction.payload.patchPath;
      store.dispatch(deletePatch(patchPath));

      const project = getProject(store.getState());
      const maybeDeletedPatch = getPatchByPath(patchPath, project);

      assert.isTrue(Maybe.isNothing(maybeDeletedPatch));
    });
  });

  describe('Node management', () => {
    let store = null;
    let testPatchPath = '';

    beforeEach(
      () => {
        store = createStore(generateReducers(), initialState, applyMiddleware(thunk));
        const addPatchAction = store.dispatch(addPatch('test-patch'));
        testPatchPath = addPatchAction.payload.patchPath;
      }
    );

    it('should add a node', () => {
      const nodeId = store.dispatch(addNode('xod/patch-nodes/input-number', { x: 0, y: 0 }, testPatchPath));

      const maybeNode = R.compose(
        getNodeById(nodeId),
        R.view(lensPatch(testPatchPath)),
        getProject
      )(store.getState());

      assert.isTrue(Maybe.isJust(maybeNode));
    });
    it('should move a node', () => {
      const nodeId = store.dispatch(addNode('xod/patch-nodes/input-number', { x: 0, y: 0 }, testPatchPath));
      const desiredPosition = { x: 111, y: 222 };
      store.dispatch(moveNode(nodeId, desiredPosition));

      const maybeNode = R.compose(
        getNodeById(nodeId),
        R.view(lensPatch(testPatchPath)),
        getProject
      )(store.getState());

      const actualPosition = Maybe.maybe({}, getNodePosition, maybeNode);

      assert.deepEqual(
        desiredPosition,
        actualPosition
      );
    });
    it('should update node label', () => {
      const nodeId = store.dispatch(addNode('xod/patch-nodes/input-number', { x: 0, y: 0 }, testPatchPath));
      const desiredLabel = 'desired label';
      store.dispatch(updateNodeProperty(nodeId, 'property', 'label', desiredLabel));

      const maybeNode = R.compose(
        getNodeById(nodeId),
        R.view(lensPatch(testPatchPath)),
        getProject
      )(store.getState());

      const actualLabel = Maybe.maybe({}, getNodeLabel, maybeNode);

      assert.deepEqual(
        desiredLabel,
        actualLabel
      );
    });
    it('should update node\'s pin value ', () => {
      const nodeId = store.dispatch(addNode('xod/patch-nodes/output-number', { x: 0, y: 0 }, testPatchPath));
      const pinKey = '__in__';
      const desiredPinValue = 42;
      store.dispatch(updateNodeProperty(nodeId, 'pin', pinKey, desiredPinValue));

      const maybeNode = R.compose(
        getNodeById(nodeId),
        R.view(lensPatch(testPatchPath)),
        getProject
      )(store.getState());

      const maybePinValue = maybeNode.chain(getPinCurriedValue(pinKey));

      const actualPinValue = Maybe.maybe({}, R.identity, maybePinValue);

      assert.deepEqual(
        desiredPinValue,
        actualPinValue
      );
    });
    it('should delete a node', () => {
      const nodeId = store.dispatch(addNode('xod/patch-nodes/input-number', { x: 0, y: 0 }, testPatchPath));
      store.dispatch(deleteNode(nodeId));

      const maybeNode = R.compose(
        getNodeById(nodeId),
        R.view(lensPatch(testPatchPath)),
        getProject
      )(store.getState());

      assert.isTrue(Maybe.isNothing(maybeNode));
    });
  });

  describe('Link management', () => {
    let store = null;
    let testPatchPath = '';
    let inNodeId = '';
    let outNodeId = '';

    beforeEach(
      () => {
        store = createStore(generateReducers(), initialState, applyMiddleware(thunk));
        const addPatchAction = store.dispatch(addPatch('test-patch'));
        testPatchPath = addPatchAction.payload.patchPath;
        inNodeId = store.dispatch(addNode('xod/patch-nodes/input-number', { x: 100, y: 100 }, testPatchPath));
        outNodeId = store.dispatch(addNode('xod/patch-nodes/output-number', { x: 500, y: 500 }, testPatchPath));
      }
    );

    it('should add a link', () => {
      store.dispatch(addLink(
        { nodeId: inNodeId, pinKey: '__out__' },
        { nodeId: outNodeId, pinKey: '__in__' }
      ));

      const links = R.compose(
        listLinks,
        R.view(lensPatch(testPatchPath)),
        getProject
      )(store.getState());

      assert.equal(1, links.length);
    });

    it('should delete a link', () => {
      store.dispatch(addLink(
        { nodeId: inNodeId, pinKey: '__out__' },
        { nodeId: outNodeId, pinKey: '__in__' }
      ));

      const linkId = R.compose(
        getLinkId,
        R.head,
        listLinks,
        R.view(lensPatch(testPatchPath)),
        getProject
      )(store.getState());

      store.dispatch(deleteLink(linkId, testPatchPath));

      const links = R.compose(
        listLinks,
        R.view(lensPatch(testPatchPath)),
        getProject
      )(store.getState());

      assert.equal(0, links.length);
    });
  });
});

<|endoftext|>


<commit_before>
angular.module('os.biospecimen.cp',
  [
    'ui.router',
    'os.biospecimen.cp.list',
    'os.biospecimen.cp.addedit',
    'os.biospecimen.cp.import',
    'os.biospecimen.cp.detail',
    'os.biospecimen.cp.consents',
    'os.biospecimen.cp.events',
    'os.biospecimen.cp.specimens',
    'os.biospecimen.cp.catalog'
  ])

  .config(function($stateProvider) {
    $stateProvider
      .state('cps', {
        url: '/cps',
        abstract: true,
        template: '

',
        controller: function($scope) {
          // Collection Protocol Authorization Options
          $scope.cpResource = {
            createOpts: {resource: 'CollectionProtocol', operations: ['Create']},
            updateOpts: {resource: 'CollectionProtocol', operations: ['Update']},
            deleteOpts: {resource: 'CollectionProtocol', operations: ['Delete']}
          }

          $scope.codingEnabled = $scope.global.appProps.cp_coding_enabled;
        },
        parent: 'signed-in'
      })
      .state('cp-list', {
        url: '',
        templateUrl: 'modules/biospecimen/cp/list.html',
        controller: 'CpListCtrl',
        parent: 'cps',
        resolve: {
          cpList: function(CollectionProtocol) {
            return CollectionProtocol.query();
          },

          view: function($rootScope, $state, cpList) {
            if ($rootScope.stateChangeInfo.fromState.name == 'login') {
              if (cpList.length == 1) {
                $state.go('participant-list', {cpId: cpList[0].id});
              } else if (cpList.length == 0) {
                $state.go('home');
              }
            }
          }
        }
      })
      .state('import-biospecimen-objs', {
        url: '/bulk-import?objectType&entityType',
        templateUrl: 'modules/common/import/add.html',
        controller: 'ImportObjectCtrl',
        resolve: {
          importDetail: function($stateParams) {
            var objectType = $stateParams.objectType;
            var importDetail = {
              breadcrumbs: [{state: 'cp-list', title: 'cp.list'}],
              objectType: objectType,
              onSuccess: {state: 'cp-list', params: {}},
            };

            if (objectType == 'cpr') {
              angular.extend(importDetail, {
                title:          'participant.bulk_reg_participants',
                showImportType: false,
                importType:     'CREATE'
              });
            } else if (objectType == 'participant') {
              angular.extend(importDetail, {
                title:          'participant.bulk_update_participants',
                showImportType: false,
                importType:     'UPDATE'
              });
            } else if (objectType == 'visit') {
              importDetail.title = 'visits.bulk_import';
            } else if (objectType == 'specimen') {
              importDetail.title = 'specimens.bulk_import';
            } else if (objectType == 'specimenAliquot') {
              angular.extend(importDetail, {
                title:          'specimens.bulk_import_aliquots',
                showImportType: false,
                importType    : 'CREATE'
              });
            } else if (objectType == 'specimenDerivative') {
              angular.extend(importDetail, {
                title:          'specimens.bulk_import_derivatives',
                showImportType: false,
                importType    : 'CREATE'
              });
            } else if (objectType == 'extensions') {
              var entityType = $stateParams.entityType;
              var title = undefined;
              if (entityType == 'Participant') {
                title = 'extensions.bulk_import_participant_extns';
              } else if (entityType == 'SpecimenCollectionGroup') {
                title = 'extensions.bulk_import_visit_extns';
              } else if (entityType == 'Specimen') {
                title = 'extensions.bulk_import_specimen_extns';
              } else if (entityType == 'SpecimenEvent') {
                title = 'extensions.bulk_import_specimen_events';
              }

              angular.extend(importDetail, {
                title: title,
                entityType: entityType
              });
            }

            return importDetail;
          }
        },
        parent: 'cps'
      })
      .state('biospecimen-obj-import-jobs', {
        url: '/bulk-import-jobs',
        templateUrl: 'modules/common/import/list.html',
        controller: 'ImportJobsListCtrl',
        resolve: {
          importDetail: function() {
            return {
              breadcrumbs: [{state: 'cp-list', title: 'cp.list'}],
              title: 'cp.bulk_import_biospecimen_obj_jobs',
              objectTypes: [
                'cpr', 'participant', 'visit',
                'specimen', 'specimenDerivative', 'specimenAliquot',
                'extensions'
              ]
            }
          }
        },
        parent: 'cps'
      })
      .state('cp-addedit', {
        url: '/addedit/:cpId',
        templateUrl: 'modules/biospecimen/cp/addedit.html',
        resolve: {
          cp: function($stateParams, CollectionProtocol) {
            if ($stateParams.cpId) {
              return CollectionProtocol.getById($stateParams.cpId);
            }
            return new CollectionProtocol();
          },
          extensionCtxt: function(CollectionProtocol) {
            return CollectionProtocol.getExtensionCtxt();
          }
        },
        controller: 'CpAddEditCtrl',
        parent: 'cps'
      })
      .state('cp-import', {
        url: '/import',
        templateUrl: 'modules/biospecimen/cp/import.html',
        controller: 'CpImportCtrl',
        parent: 'cps'
      })
      .state('cp-detail', {
        url: '/:cpId',
        templateUrl: 'modules/biospecimen/cp/detail.html',
        parent: 'cps',
        resolve: {
          cp: function($stateParams, CollectionProtocol) {
            return CollectionProtocol.getById($stateParams.cpId);
          }
        },
        controller: 'CpDetailCtrl',
        breadcrumb: {
          title: '{{cp.title}}',
          state: 'cp-detail.overview'
        }
      })
      .state('cp-detail.overview', {
        url: '/overview',
        templateUrl: 'modules/biospecimen/cp/overview.html',
        parent: 'cp-detail'
      })
      .state('cp-detail.consents', {
        url: '/consents',
        templateUrl: 'modules/biospecimen/cp/consents.html',
        parent: 'cp-detail',
        resolve: {
          consentTiers: function(cp) {
            return cp.getConsentTiers();
          }
        },
        controller: 'CpConsentsCtrl'
      })
      .state('cp-detail.events', {
        templateUrl: 'modules/biospecimen/cp/events.html',
        parent: 'cp-detail',
        resolve: {
          events: function($stateParams, cp, CollectionProtocolEvent) {
            return CollectionProtocolEvent.listFor(cp.id);
          }
        },
        controller: 'CpEventsCtrl'
      })
      .state('cp-detail.specimen-requirements', {
        url: '/specimen-requirements?eventId',
        templateUrl: 'modules/biospecimen/cp/specimens.html',
        parent: 'cp-detail.events',
        resolve: {
          specimenRequirements: function($stateParams, SpecimenRequirement) {
            var eventId = $stateParams.eventId;
            if (!eventId) {
              return [];
            }

            return SpecimenRequirement.listFor(eventId);
          }
        },
        controller: 'CpSpecimensCtrl'
      })
      .state('cp-detail.catalog-settings', {
        url: '/catalog-settings',
        templateUrl: 'modules/biospecimen/cp/catalog-settings.html',
        parent: 'cp-detail',
        resolve: {
          catalogSetting: function(cp) {
            if (cp.catalogSetting) {
              return cp.catalogSetting;
            }

            return cp.getCatalogSetting().then(
              function(setting) {
                cp.catalogSetting = setting || {};
              }
            );
          }
        },
        controller: 'CpCatalogSettingsCtrl'
      })
    });

<commit_msg>OPSMN-2704: The CP list view was broken in that PI name and participant/specimen counts were not displayed. The root cause was CP list API was invoked to obtain only CP short title and title<commit_after>
angular.module('os.biospecimen.cp',
  [
    'ui.router',
    'os.biospecimen.cp.list',
    'os.biospecimen.cp.addedit',
    'os.biospecimen.cp.import',
    'os.biospecimen.cp.detail',
    'os.biospecimen.cp.consents',
    'os.biospecimen.cp.events',
    'os.biospecimen.cp.specimens',
    'os.biospecimen.cp.catalog'
  ])

  .config(function($stateProvider) {
    $stateProvider
      .state('cps', {
        url: '/cps',
        abstract: true,
        template: '

',
        controller: function($scope) {
          // Collection Protocol Authorization Options
          $scope.cpResource = {
            createOpts: {resource: 'CollectionProtocol', operations: ['Create']},
            updateOpts: {resource: 'CollectionProtocol', operations: ['Update']},
            deleteOpts: {resource: 'CollectionProtocol', operations: ['Delete']}
          }

          $scope.codingEnabled = $scope.global.appProps.cp_coding_enabled;
        },
        parent: 'signed-in'
      })
      .state('cp-list', {
        url: '',
        templateUrl: 'modules/biospecimen/cp/list.html',
        controller: 'CpListCtrl',
        parent: 'cps',
        resolve: {
          cpList: function(CollectionProtocol) {
            return CollectionProtocol.list();
          },

          view: function($rootScope, $state, cpList) {
            if ($rootScope.stateChangeInfo.fromState.name == 'login') {
              if (cpList.length == 1) {
                $state.go('participant-list', {cpId: cpList[0].id});
              } else if (cpList.length == 0) {
                $state.go('home');
              }
            }
          }
        }
      })
      .state('import-biospecimen-objs', {
        url: '/bulk-import?objectType&entityType',
        templateUrl: 'modules/common/import/add.html',
        controller: 'ImportObjectCtrl',
        resolve: {
          importDetail: function($stateParams) {
            var objectType = $stateParams.objectType;
            var importDetail = {
              breadcrumbs: [{state: 'cp-list', title: 'cp.list'}],
              objectType: objectType,
              onSuccess: {state: 'cp-list', params: {}},
            };

            if (objectType == 'cpr') {
              angular.extend(importDetail, {
                title:          'participant.bulk_reg_participants',
                showImportType: false,
                importType:     'CREATE'
              });
            } else if (objectType == 'participant') {
              angular.extend(importDetail, {
                title:          'participant.bulk_update_participants',
                showImportType: false,
                importType:     'UPDATE'
              });
            } else if (objectType == 'visit') {
              importDetail.title = 'visits.bulk_import';
            } else if (objectType == 'specimen') {
              importDetail.title = 'specimens.bulk_import';
            } else if (objectType == 'specimenAliquot') {
              angular.extend(importDetail, {
                title:          'specimens.bulk_import_aliquots',
                showImportType: false,
                importType    : 'CREATE'
              });
            } else if (objectType == 'specimenDerivative') {
              angular.extend(importDetail, {
                title:          'specimens.bulk_import_derivatives',
                showImportType: false,
                importType    : 'CREATE'
              });
            } else if (objectType == 'extensions') {
              var entityType = $stateParams.entityType;
              var title = undefined;
              if (entityType == 'Participant') {
                title = 'extensions.bulk_import_participant_extns';
              } else if (entityType == 'SpecimenCollectionGroup') {
                title = 'extensions.bulk_import_visit_extns';
              } else if (entityType == 'Specimen') {
                title = 'extensions.bulk_import_specimen_extns';
              } else if (entityType == 'SpecimenEvent') {
                title = 'extensions.bulk_import_specimen_events';
              }

              angular.extend(importDetail, {
                title: title,
                entityType: entityType
              });
            }

            return importDetail;
          }
        },
        parent: 'cps'
      })
      .state('biospecimen-obj-import-jobs', {
        url: '/bulk-import-jobs',
        templateUrl: 'modules/common/import/list.html',
        controller: 'ImportJobsListCtrl',
        resolve: {
          importDetail: function() {
            return {
              breadcrumbs: [{state: 'cp-list', title: 'cp.list'}],
              title: 'cp.bulk_import_biospecimen_obj_jobs',
              objectTypes: [
                'cpr', 'participant', 'visit',
                'specimen', 'specimenDerivative', 'specimenAliquot',
                'extensions'
              ]
            }
          }
        },
        parent: 'cps'
      })
      .state('cp-addedit', {
        url: '/addedit/:cpId',
        templateUrl: 'modules/biospecimen/cp/addedit.html',
        resolve: {
          cp: function($stateParams, CollectionProtocol) {
            if ($stateParams.cpId) {
              return CollectionProtocol.getById($stateParams.cpId);
            }
            return new CollectionProtocol();
          },
          extensionCtxt: function(CollectionProtocol) {
            return CollectionProtocol.getExtensionCtxt();
          }
        },
        controller: 'CpAddEditCtrl',
        parent: 'cps'
      })
      .state('cp-import', {
        url: '/import',
        templateUrl: 'modules/biospecimen/cp/import.html',
        controller: 'CpImportCtrl',
        parent: 'cps'
      })
      .state('cp-detail', {
        url: '/:cpId',
        templateUrl: 'modules/biospecimen/cp/detail.html',
        parent: 'cps',
        resolve: {
          cp: function($stateParams, CollectionProtocol) {
            return CollectionProtocol.getById($stateParams.cpId);
          }
        },
        controller: 'CpDetailCtrl',
        breadcrumb: {
          title: '{{cp.title}}',
          state: 'cp-detail.overview'
        }
      })
      .state('cp-detail.overview', {
        url: '/overview',
        templateUrl: 'modules/biospecimen/cp/overview.html',
        parent: 'cp-detail'
      })
      .state('cp-detail.consents', {
        url: '/consents',
        templateUrl: 'modules/biospecimen/cp/consents.html',
        parent: 'cp-detail',
        resolve: {
          consentTiers: function(cp) {
            return cp.getConsentTiers();
          }
        },
        controller: 'CpConsentsCtrl'
      })
      .state('cp-detail.events', {
        templateUrl: 'modules/biospecimen/cp/events.html',
        parent: 'cp-detail',
        resolve: {
          events: function($stateParams, cp, CollectionProtocolEvent) {
            return CollectionProtocolEvent.listFor(cp.id);
          }
        },
        controller: 'CpEventsCtrl'
      })
      .state('cp-detail.specimen-requirements', {
        url: '/specimen-requirements?eventId',
        templateUrl: 'modules/biospecimen/cp/specimens.html',
        parent: 'cp-detail.events',
        resolve: {
          specimenRequirements: function($stateParams, SpecimenRequirement) {
            var eventId = $stateParams.eventId;
            if (!eventId) {
              return [];
            }

            return SpecimenRequirement.listFor(eventId);
          }
        },
        controller: 'CpSpecimensCtrl'
      })
      .state('cp-detail.catalog-settings', {
        url: '/catalog-settings',
        templateUrl: 'modules/biospecimen/cp/catalog-settings.html',
        parent: 'cp-detail',
        resolve: {
          catalogSetting: function(cp) {
            if (cp.catalogSetting) {
              return cp.catalogSetting;
            }

            return cp.getCatalogSetting().then(
              function(setting) {
                cp.catalogSetting = setting || {};
              }
            );
          }
        },
        controller: 'CpCatalogSettingsCtrl'
      })
    });

<|endoftext|>


<commit_before>/*
 * Copyrights phwang
 * Written by Paul Hwang
 * File name: link_module.js
 */

module.exports = {
    malloc: function (link_mgr_val, my_name_val, link_id_val) {
        return new LinkObject(link_mgr_val, my_name_val, link_id_val);
    },
};

function LinkObject(link_mgr_val, my_name_val, link_id_val) {
    "use strict";
    this.theLinkMgrObject  = link_mgr_val;

    this.objectName = function () {
        return "LinkObject";
    };

    this.linkMgrObject = function () {
        return this.theLinkMgrObject;
    };

    this.FibreObject = function () {
        return this.linkMgrObject().FibreObject();
    };

    this.utilObject = function () {
        return this.linkMgrObject().utilObject();
    };

    this.linkId = function () {
        return this.theLinkId;
    };

    this.setLinkId = function (val) {
        this.theLinkId = val;
    };

    this.myName = function () {
        return this.theMyName;
    };

    this.setMyName = function (val) {
        this.theMyName = val;
    };

    this.receiveQueue = function () {
        return this.theReceiveQueue;
    };

    this.receiveRing = function () {
        return this.theReceiveRing;
    };

    this.resetIt = function (my_name_val, link_id_val) {
        this.theLinkId = link_id_val;
        this.theMyName = my_name_val;
        this.up_seq = 0;
        this.down_seq = 0;
        this.theReceiveQueue = this.utilObject().mallocQueue();
        this.theReceiveRing = this.utilObject().mallocRing();
        this.keep_alive_timer = this.resetTimeout();
};

    this.resetKeepAliveTimer = function () {
        this.debug(false, "keepAlive", "my_name=" + this.my_name + " link_id=" + this.link_id);
        this.keep_alive_timer = this.resetTimeout();
    };

    this.resetTimeout = function () {
        if (this.keep_alive_timer) {
            clearInterval(this.keep_alive_timer);
        }
        this.debug(false, "resetTimeout", "my_name=" + this.my_name + " link_id=" + this.link_id);
        var time_out = setInterval(function (link_val) {
            console.log("resetTimeout(***timeout occurs)", "my_name=" + link_val.myName() + " link_id=" + link_val.linkId());
            clearInterval(link_val.keep_alive_timer);
            link_val.linkMgrObject().freeLink(link_val);
        }, 20000, this);
        return time_out;
    };

    this.debug = function (debug_val, str1_val, str2_val) {
        if (debug_val) {
            logit(str1_val, "==" + str2_val);
        }
    };

    this.logit = function (str1_val, str2_val) {
        this.linkMgrObject().logit(this.objectName() + "." + str1_val, str2_val);
    };

    this.abend = function (str1_val, str2_val) {
        this.linkMgrObject().abend(this.objectName() + "." + str1_val, str2_val);
    };

    this.resetIt(my_name_val, link_id_val);
}
<commit_msg>timer<commit_after>/*
 * Copyrights phwang
 * Written by Paul Hwang
 * File name: link_module.js
 */

module.exports = {
    malloc: function (link_mgr_val, my_name_val, link_id_val) {
        return new LinkObject(link_mgr_val, my_name_val, link_id_val);
    },
};

function LinkObject(link_mgr_val, my_name_val, link_id_val) {
    "use strict";
    this.theLinkMgrObject  = link_mgr_val;

    this.objectName = function () {
        return "LinkObject";
    };

    this.linkMgrObject = function () {
        return this.theLinkMgrObject;
    };

    this.FibreObject = function () {
        return this.linkMgrObject().FibreObject();
    };

    this.utilObject = function () {
        return this.linkMgrObject().utilObject();
    };

    this.linkId = function () {
        return this.theLinkId;
    };

    this.setLinkId = function (val) {
        this.theLinkId = val;
    };

    this.myName = function () {
        return this.theMyName;
    };

    this.setMyName = function (val) {
        this.theMyName = val;
    };

    this.keepAliveTimer = function () {
        return this.theKeepAliveTimer;
    };

    this.setKeepAliveTimer = function (val) {
        this.theKeepAliveTimer = val;
    };

    this.receiveQueue = function () {
        return this.theReceiveQueue;
    };

    this.receiveRing = function () {
        return this.theReceiveRing;
    };

    this.resetIt = function (my_name_val, link_id_val) {
        this.theLinkId = link_id_val;
        this.theMyName = my_name_val;
        this.up_seq = 0;
        this.down_seq = 0;
        this.theReceiveQueue = this.utilObject().mallocQueue();
        this.theReceiveRing = this.utilObject().mallocRing();
        this.theKeepAliveTimer = this.resetTimeout();
    };

    this.resetKeepAliveTimer = function () {
        this.debug(false, "keepAlive", "my_name=" + this.my_name + " link_id=" + this.link_id);
        this.setKeepAliveTimer(this.resetTimeout());
    };

    this.resetTimeout = function () {
        if (this.keepAliveTimer()) {
            clearInterval(this.keepAliveTimer());
        }
        this.debug(false, "resetTimeout", "my_name=" + this.my_name + " link_id=" + this.link_id);
        var time_out = setInterval(function (link_val) {
            console.log("resetTimeout(***timeout occurs)", "my_name=" + link_val.myName() + " link_id=" + link_val.linkId());
            clearInterval(link_val.keepAliveTimer());
            link_val.linkMgrObject().freeLink(link_val);
        }, 20000, this);
        return time_out;
    };

    this.debug = function (debug_val, str1_val, str2_val) {
        if (debug_val) {
            logit(str1_val, "==" + str2_val);
        }
    };

    this.logit = function (str1_val, str2_val) {
        this.linkMgrObject().logit(this.objectName() + "." + str1_val, str2_val);
    };

    this.abend = function (str1_val, str2_val) {
        this.linkMgrObject().abend(this.objectName() + "." + str1_val, str2_val);
    };

    this.resetIt(my_name_val, link_id_val);
}
<|endoftext|>


<commit_before>'use strict';

const assert = require('assert');
const Joi = require('joi');
const nrk = require('../');

describe.skip('categories()', function describe() {
  it('returns list of categories', function it(done) {
    nrk.tv.mobil.categories(function categoriesCb(err, data, resp) {
      assert.ifError(err);
      assert.equal(resp.statusCode, 200);

      const schema = Joi.array().items(Joi.object().keys({
        categoryId: Joi.string().example('dokumentar'),
        displayValue: Joi.string().example('Dokumentar'),
        availableFilters: Joi.array().items(Joi.string().valid(
          [ 'POPULAR', 'RECOMMENDED', 'RECENT' ]
        )),
      }));

      Joi.validate(data, schema, done);
    });
  });
});

describe('programs()', function describe() {
  it('returns list of programs', function it(done) {
    this.timeout(10000);

    nrk.tv.mobil.programs(null, function programsCb(err, data, resp) {
      assert.ifError(err);
      assert.equal(resp.statusCode, 200);

      const schema = Joi.array().items(Joi.object().keys({
        title: Joi.string().example('Diktaturet'),
        description: Joi.string().allow(''),
        episodeNumberOrDate: Joi.string().example('7:8'),
        mediaUrl: Joi.string(),
        programId: Joi.string().example('mynt14000715'),
        seriesId: Joi.string().example('diktaturet'),
        seriesImageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
        imageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
        plugImageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
        source: Joi.string().example('plugs'),
        category: Joi.object().keys({
          categoryId: Joi.string().example('underholdning'),
          displayValue: Joi.string().example('Underholdning'),
          priority: Joi.number().integer(),
        }),
        seasonId: Joi.number().integer(),
        promoText: Joi.string(),
        usageRights: Joi.object().keys({
          availableFrom: Joi.number().integer(),
          availableTo: Joi.number().integer(),
          hasNoRights: Joi.boolean(),
          geoblocked: Joi.boolean(),
        }),
        liveProgramMetadata: Joi.object().keys({
          startTime: Joi.number().integer(),
          endTime: Joi.number().integer(),
          nextProgram: Joi.string(),
        }),
        plugTitle: Joi.string(),
        legalAge: Joi.string().valid(['A', '6', '9', '12', '15', '18']),
        labelText: Joi.string(),
        channelId: Joi.string(),
        epgEntries: Joi.array().items(Joi.object()),
        size: Joi.string().valid('small', 'medium', 'large', 'random'),
        applicationType: Joi.string().example('bigscreen'),
        duration: Joi.number().integer(),
        isAvailable: Joi.boolean(),
      }));

      Joi.validate(data, schema, done);
    });
  });

  it('returns single program', function it(done) {
    this.timeout(10000);

    nrk.tv.mobil.programs('mynt14000715', function programsCb(err, data, resp) {
      assert.ifError(err);
      assert.equal(resp.statusCode, 200);

      const schema = Joi.object().keys({
        title: Joi.string().example('Diktaturet'),
        description: Joi.string().allow(''),
        episodeNumberOrDate: Joi.string().example('7:8'),
        mediaUrl: Joi.string(),
        programId: Joi.string().example('mynt14000715'),
        seriesId: Joi.string().example('diktaturet'),
        seriesImageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
        imageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
        category: Joi.object().keys({
          categoryId: Joi.string().example('underholdning'),
          displayValue: Joi.string().example('Underholdning'),
          priority: Joi.number().integer(),
        }),
        seasonId: Joi.number().integer(),
        usageRights: Joi.object().keys({
          availableFrom: Joi.number().integer(),
          availableTo: Joi.number().integer(),
          hasNoRights: Joi.boolean(),
          geoblocked: Joi.boolean(),
        }),
        legalAge: Joi.string().valid(['A', '6', '9', '12', '15', '18']),
        numberOfAvailableEpisodes: Joi.number().integer(),
        epgEntries: Joi.array(),
        duration: Joi.number().integer(),
        isAvailable: Joi.boolean(),
        fullTitle: Joi.string().example('Diktaturet 7:8'),
        shortIndexPoints: Joi.array().items(Joi.object().keys({
          title: Joi.string().example('Velkommen til Diktaturet'),
          startPoint: Joi.number().integer(),
          partId: Joi.number().integer(),
          part: Joi.number().integer(),
        })),
        hasSubtitles: Joi.boolean(),
        hasReview: Joi.boolean(),
        series: Joi.object().keys({
          seriesId: Joi.string().example('diktaturet'),
          title: Joi.string().example('Diktaturet'),
          description: Joi.string(),
          seasonIds: Joi.array().items(Joi.object().keys({
            id: Joi.number().integer(),
            name: Joi.string().example('Sesong 1'),
          })),
          imageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
          category: Joi.object().keys({
            categoryId: Joi.string().example('underholdning'),
            displayValue: Joi.string().example('Underholdning'),
            priority: Joi.number().integer(),
          }),
          programs: Joi.array().items(Joi.object().keys({
            title: Joi.string().example('Diktaturet'),
            description: Joi.string(),
            episodeNumberOrDate: Joi.string().example('7:8'),
            programId: Joi.string().example('mynt14000715'),
            seriesId: Joi.string().example('diktaturet'),
            imageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
            category: Joi.object().keys({
              categoryId: Joi.string().example('underholdning'),
              displayValue: Joi.string().example('Underholdning'),
              priority: Joi.number().integer(),
            }),
            seasonId: Joi.number().integer(),
            usageRights: Joi.object().keys({
              availableFrom: Joi.number().integer(),
              availableTo: Joi.number().integer(),
              hasNoRights: Joi.boolean(),
              geoblocked: Joi.boolean(),
            }),
            legalAge: Joi.string().valid(['A', '6', '9', '12', '15', '18']),
            duration: Joi.number().integer(),
            isAvailable: Joi.boolean(),
          })),
        }),
        contributors: Joi.array(),
        relativeOriginUrl: Joi.string(),
        more: Joi.array().label('Related Shows'),
        statistics: Joi.object(),
      });

      Joi.validate(data, schema, done);
    });
  });
});

describe('series()', function describe() {
  it('returns single series', function it(done) {
    this.timeout(10000);

    nrk.tv.mobil.series('solsikkesmuget-19', function seriesCb(err, data, resp) {
      assert.ifError(err);
      assert.equal(resp.statusCode, 200);

      const schema = Joi.object().keys({
        seriesId: Joi.string().example('solsikkesmuget-19'),
        title: Joi.string().example('Solsikkesmuget 19'),
        description: Joi.string(),
        seasonIds: Joi.array().items(Joi.object().keys({
          id: Joi.number().integer().example(35359),
          name: Joi.string().example('Sesong 1'),
        })),
        imageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
        category: Joi.object().keys({
          categoryId: Joi.string().example('barn'),
          displayValue: Joi.string().example('Barn'),
        }),
        programs: Joi.array().items(Joi.object().keys({
          title: Joi.string().example('Solsikkesmuget 19'),
          description: Joi.string(),
          episodeNumberOrDate: Joi.string().example('24:24'),
          programId: Joi.string().example('msui12008313'),
          seriesId: Joi.string().example('solsikkesmuget-19'),
          imageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
          category: Joi.object().keys({
            categoryId: Joi.string().example('barn'),
            displayValue: Joi.string().example('Barn'),
          }),
          seasonId: Joi.number().integer().example(35359),
          usageRights: Joi.object().keys({
            availableFrom: Joi.number().integer(),
            availableTo: Joi.number().integer(),
            hasNoRights: Joi.boolean(),
            geoblocked: Joi.boolean(),
          }).allow(null),
          legalAge: Joi.string().valid(['A', '6', '9', '12', '15', '18']),
          duration: Joi.number().allow(null),
          isAvailable: Joi.boolean(),
        })),
      });

      Joi.validate(data, schema, done);
    });
  });
});

describe('search()', function describe() {
  it('returns matching list of programs', function it(done) {
    this.timeout(10000);

    nrk.tv.mobil.search('Side om side', function searchCb(err, data, resp) {
      assert.ifError(err);
      assert.equal(resp.statusCode, 200);

      const schema = Joi.object().keys({
        metaData: Joi.object(),
        hits: Joi.array().items(Joi.object().keys({
          type: Joi.string().valid('serie', 'episode', 'program'),
          hit: Joi.object().keys({
            objectId: Joi.string().example('side-om-side'),
            title: Joi.string().example('Side om side'),
            description: Joi.string(),
            imageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
            seasons: Joi.any().allow(null),
            category: Joi.object(),
            duration: Joi.number().allow(null),
            expires: Joi.any().allow(null),
            seriesId: Joi.string().allow(null).example('side-om-side'),
            seasonNumber: Joi.string().allow(null),
            seriesTitle: Joi.string().allow(null),
            episodeNumberOrDate: Joi.string().allow(null).example('7:8'),
            episodeTitle: Joi.string().allow(null),
            aldersgrense: Joi.number().integer().allow(null),
            usageRights: Joi.object().keys({
              availableFrom: Joi.number().integer(),
              availableTo: Joi.number().integer(),
              hasNoRights: Joi.boolean(),
              geoblocked: Joi.boolean(),
            }).allow(null),
            programId: Joi.string().allow(null).example('mynt14000715'),
          }),
          highlights: Joi.array().allow(null),
        })),
      });

      Joi.validate(data, schema, done);
    });
  });
});
<commit_msg>Revert "Skip nrk.tv.mobil.categories() test cases"<commit_after>'use strict';

const assert = require('assert');
const Joi = require('joi');
const nrk = require('../');

describe('categories()', function describe() {
  it('returns list of categories', function it(done) {
    nrk.tv.mobil.categories(function categoriesCb(err, data, resp) {
      assert.ifError(err);
      assert.equal(resp.statusCode, 200);

      const schema = Joi.array().items(Joi.object().keys({
        categoryId: Joi.string().example('dokumentar'),
        displayValue: Joi.string().example('Dokumentar'),
        availableFilters: Joi.array().items(Joi.string().valid(
          [ 'POPULAR', 'RECOMMENDED', 'RECENT' ]
        )),
      }));

      Joi.validate(data, schema, done);
    });
  });
});

describe('programs()', function describe() {
  it('returns list of programs', function it(done) {
    this.timeout(10000);

    nrk.tv.mobil.programs(null, function programsCb(err, data, resp) {
      assert.ifError(err);
      assert.equal(resp.statusCode, 200);

      const schema = Joi.array().items(Joi.object().keys({
        title: Joi.string().example('Diktaturet'),
        description: Joi.string().allow(''),
        episodeNumberOrDate: Joi.string().example('7:8'),
        mediaUrl: Joi.string(),
        programId: Joi.string().example('mynt14000715'),
        seriesId: Joi.string().example('diktaturet'),
        seriesImageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
        imageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
        plugImageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
        source: Joi.string().example('plugs'),
        category: Joi.object().keys({
          categoryId: Joi.string().example('underholdning'),
          displayValue: Joi.string().example('Underholdning'),
          priority: Joi.number().integer(),
        }),
        seasonId: Joi.number().integer(),
        promoText: Joi.string(),
        usageRights: Joi.object().keys({
          availableFrom: Joi.number().integer(),
          availableTo: Joi.number().integer(),
          hasNoRights: Joi.boolean(),
          geoblocked: Joi.boolean(),
        }),
        liveProgramMetadata: Joi.object().keys({
          startTime: Joi.number().integer(),
          endTime: Joi.number().integer(),
          nextProgram: Joi.string(),
        }),
        plugTitle: Joi.string(),
        legalAge: Joi.string().valid(['A', '6', '9', '12', '15', '18']),
        labelText: Joi.string(),
        channelId: Joi.string(),
        epgEntries: Joi.array().items(Joi.object()),
        size: Joi.string().valid('small', 'medium', 'large', 'random'),
        applicationType: Joi.string().example('bigscreen'),
        duration: Joi.number().integer(),
        isAvailable: Joi.boolean(),
      }));

      Joi.validate(data, schema, done);
    });
  });

  it('returns single program', function it(done) {
    this.timeout(10000);

    nrk.tv.mobil.programs('mynt14000715', function programsCb(err, data, resp) {
      assert.ifError(err);
      assert.equal(resp.statusCode, 200);

      const schema = Joi.object().keys({
        title: Joi.string().example('Diktaturet'),
        description: Joi.string().allow(''),
        episodeNumberOrDate: Joi.string().example('7:8'),
        mediaUrl: Joi.string(),
        programId: Joi.string().example('mynt14000715'),
        seriesId: Joi.string().example('diktaturet'),
        seriesImageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
        imageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
        category: Joi.object().keys({
          categoryId: Joi.string().example('underholdning'),
          displayValue: Joi.string().example('Underholdning'),
          priority: Joi.number().integer(),
        }),
        seasonId: Joi.number().integer(),
        usageRights: Joi.object().keys({
          availableFrom: Joi.number().integer(),
          availableTo: Joi.number().integer(),
          hasNoRights: Joi.boolean(),
          geoblocked: Joi.boolean(),
        }),
        legalAge: Joi.string().valid(['A', '6', '9', '12', '15', '18']),
        numberOfAvailableEpisodes: Joi.number().integer(),
        epgEntries: Joi.array(),
        duration: Joi.number().integer(),
        isAvailable: Joi.boolean(),
        fullTitle: Joi.string().example('Diktaturet 7:8'),
        shortIndexPoints: Joi.array().items(Joi.object().keys({
          title: Joi.string().example('Velkommen til Diktaturet'),
          startPoint: Joi.number().integer(),
          partId: Joi.number().integer(),
          part: Joi.number().integer(),
        })),
        hasSubtitles: Joi.boolean(),
        hasReview: Joi.boolean(),
        series: Joi.object().keys({
          seriesId: Joi.string().example('diktaturet'),
          title: Joi.string().example('Diktaturet'),
          description: Joi.string(),
          seasonIds: Joi.array().items(Joi.object().keys({
            id: Joi.number().integer(),
            name: Joi.string().example('Sesong 1'),
          })),
          imageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
          category: Joi.object().keys({
            categoryId: Joi.string().example('underholdning'),
            displayValue: Joi.string().example('Underholdning'),
            priority: Joi.number().integer(),
          }),
          programs: Joi.array().items(Joi.object().keys({
            title: Joi.string().example('Diktaturet'),
            description: Joi.string(),
            episodeNumberOrDate: Joi.string().example('7:8'),
            programId: Joi.string().example('mynt14000715'),
            seriesId: Joi.string().example('diktaturet'),
            imageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
            category: Joi.object().keys({
              categoryId: Joi.string().example('underholdning'),
              displayValue: Joi.string().example('Underholdning'),
              priority: Joi.number().integer(),
            }),
            seasonId: Joi.number().integer(),
            usageRights: Joi.object().keys({
              availableFrom: Joi.number().integer(),
              availableTo: Joi.number().integer(),
              hasNoRights: Joi.boolean(),
              geoblocked: Joi.boolean(),
            }),
            legalAge: Joi.string().valid(['A', '6', '9', '12', '15', '18']),
            duration: Joi.number().integer(),
            isAvailable: Joi.boolean(),
          })),
        }),
        contributors: Joi.array(),
        relativeOriginUrl: Joi.string(),
        more: Joi.array().label('Related Shows'),
        statistics: Joi.object(),
      });

      Joi.validate(data, schema, done);
    });
  });
});

describe('series()', function describe() {
  it('returns single series', function it(done) {
    this.timeout(10000);

    nrk.tv.mobil.series('solsikkesmuget-19', function seriesCb(err, data, resp) {
      assert.ifError(err);
      assert.equal(resp.statusCode, 200);

      const schema = Joi.object().keys({
        seriesId: Joi.string().example('solsikkesmuget-19'),
        title: Joi.string().example('Solsikkesmuget 19'),
        description: Joi.string(),
        seasonIds: Joi.array().items(Joi.object().keys({
          id: Joi.number().integer().example(35359),
          name: Joi.string().example('Sesong 1'),
        })),
        imageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
        category: Joi.object().keys({
          categoryId: Joi.string().example('barn'),
          displayValue: Joi.string().example('Barn'),
        }),
        programs: Joi.array().items(Joi.object().keys({
          title: Joi.string().example('Solsikkesmuget 19'),
          description: Joi.string(),
          episodeNumberOrDate: Joi.string().example('24:24'),
          programId: Joi.string().example('msui12008313'),
          seriesId: Joi.string().example('solsikkesmuget-19'),
          imageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
          category: Joi.object().keys({
            categoryId: Joi.string().example('barn'),
            displayValue: Joi.string().example('Barn'),
          }),
          seasonId: Joi.number().integer().example(35359),
          usageRights: Joi.object().keys({
            availableFrom: Joi.number().integer(),
            availableTo: Joi.number().integer(),
            hasNoRights: Joi.boolean(),
            geoblocked: Joi.boolean(),
          }).allow(null),
          legalAge: Joi.string().valid(['A', '6', '9', '12', '15', '18']),
          duration: Joi.number().allow(null),
          isAvailable: Joi.boolean(),
        })),
      });

      Joi.validate(data, schema, done);
    });
  });
});

describe('search()', function describe() {
  it('returns matching list of programs', function it(done) {
    this.timeout(10000);

    nrk.tv.mobil.search('Side om side', function searchCb(err, data, resp) {
      assert.ifError(err);
      assert.equal(resp.statusCode, 200);

      const schema = Joi.object().keys({
        metaData: Joi.object(),
        hits: Joi.array().items(Joi.object().keys({
          type: Joi.string().valid('serie', 'episode', 'program'),
          hit: Joi.object().keys({
            objectId: Joi.string().example('side-om-side'),
            title: Joi.string().example('Side om side'),
            description: Joi.string(),
            imageId: Joi.string().regex(/^[a-zA-Z0-9_-]+$/),
            seasons: Joi.any().allow(null),
            category: Joi.object(),
            duration: Joi.number().allow(null),
            expires: Joi.any().allow(null),
            seriesId: Joi.string().allow(null).example('side-om-side'),
            seasonNumber: Joi.string().allow(null),
            seriesTitle: Joi.string().allow(null),
            episodeNumberOrDate: Joi.string().allow(null).example('7:8'),
            episodeTitle: Joi.string().allow(null),
            aldersgrense: Joi.number().integer().allow(null),
            usageRights: Joi.object().keys({
              availableFrom: Joi.number().integer(),
              availableTo: Joi.number().integer(),
              hasNoRights: Joi.boolean(),
              geoblocked: Joi.boolean(),
            }).allow(null),
            programId: Joi.string().allow(null).example('mynt14000715'),
          }),
          highlights: Joi.array().allow(null),
        })),
      });

      Joi.validate(data, schema, done);
    });
  });
});
<|endoftext|>


<commit_before>var path = require('path');
var expect = require('chai').expect;
var pkg = path.normalize(path.join(__dirname, '..', '..', 'package.json'));

describe('cli-command:', function() {
  it('should import program specific environment variables',
    function(done) {
      // consider the flow
      // 1) verbose flag is set to false by default
      // 2) environment variable preference sets verbose to true
      // 3) Long flag negation switches off verbose
      process.env.env_verbose = true;
      var conf = {env: {merge: true}};
      var cli = require('../..')(pkg, 'env');
      var args = ['--no-verbose'];
      cli
        .configuration(conf)
        .option('-v --verbose', 'print more information')
        .parse(args);
      expect(cli.verbose).to.eql(false);
      done();
    }
  );
  it('should use environment variable value',
    function(done) {
      process.env.env_url = 'http://nodejs.org';
      var conf = {env: {merge: true}};
      var cli = require('../..')(pkg, 'env');
      var args = [];
      cli
        .configuration(conf)
        .parse(args);
      expect(cli.url).to.eql(process.env.env_url);
      done();
    }
  );
  it('should override environment variable value with option (merge all)',
    function(done) {
      var rc = '/usr/local/etc/envrc';
      process.env.env_url = 'http://nodejs.org';
      process.env.env_rc = rc;
      var value = 'http://npmjs.org';
      var conf = {env: {merge: true}};
      var cli = require('../..')(pkg, 'env');
      var args = ['--url=' + value];
      cli
        .configuration(conf)
        .option('-u --url [url]', 'a url')
        .parse(args);
      expect(cli.url).to.eql(value);
      expect(cli.rc).to.eql(rc);
      done();
    }
  );
  it('should override environment variable value with option (merge args)',
    function(done) {
      var rc = '/usr/local/etc/envrc';
      process.env.env_url = 'http://nodejs.org';
      process.env.env_rc = rc;
      var value = 'http://npmjs.org';
      var conf = {env: {merge: 'options'}};
      var cli = require('../..')(pkg, 'env');
      var args = ['--url=' + value];
      cli
        .configuration(conf)
        .option('-u --url [url]', 'a url')
        .parse(args);
      expect(cli.url).to.eql(value);
      expect(cli.rc).to.eql(undefined);
      done();
    }
  );
})
<commit_msg>Update comments in env test.<commit_after>var path = require('path');
var expect = require('chai').expect;
var pkg = path.normalize(path.join(__dirname, '..', '..', 'package.json'));

describe('cli-command:', function() {
  it('should import program specific environment variables',
    function(done) {
      // consider the flow
      // 1) verbose flag is set to false by default
      // 2) environment variable preference sets verbose to true
      // 3) Long flag negation switches off verbose
      process.env.env_verbose = true;
      var conf = {env: {merge: true}};
      var cli = require('../..')(pkg, 'env');
      var args = ['--no-verbose'];
      cli
        .configuration(conf)
        .option('-v --verbose', 'print more information')
        .parse(args);
      expect(cli.verbose).to.eql(false);
      done();
    }
  );
  it('should use environment variable value',
    function(done) {
      process.env.env_url = 'http://nodejs.org';
      var conf = {env: {merge: true}};
      var cli = require('../..')(pkg, 'env');
      var args = [];
      cli
        .configuration(conf)
        .parse(args);
      expect(cli.url).to.eql(process.env.env_url);
      done();
    }
  );
  it('should override environment variable value with option (merge all)',
    function(done) {
      var rc = '/usr/local/etc/envrc';
      process.env.env_url = 'http://nodejs.org';
      process.env.env_rc = rc;
      var value = 'http://npmjs.org';
      var conf = {env: {merge: true}};
      var cli = require('../..')(pkg, 'env');
      var args = ['--url=' + value];
      cli
        .configuration(conf)
        .option('-u --url [url]', 'a url')
        .parse(args);
      expect(cli.url).to.eql(value);
      expect(cli.rc).to.eql(rc);
      done();
    }
  );
  it('should override environment variable value with option (merge args)',
    function(done) {
      var rc = '/usr/local/etc/envrc';
      process.env.env_url = 'http://nodejs.org';
      process.env.env_rc = rc;
      var value = 'http://npmjs.org';
      var conf = {env: {merge: 'options'}};
      var cli = require('../..')(pkg, 'env');
      var args = ['--url=' + value];
      cli
        .configuration(conf)
        .option('-u --url [url]', 'a url')
        .parse(args);
      expect(cli.url).to.eql(value);
      expect(cli.rc).to.eql(undefined);
      done();
    }
  );
  // TODO: test merging into a configured stash
  // TODO: test environment variable property conflict
})
<|endoftext|>