Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/.vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
"args": ["--bail", "./dist/ECP.spec.js", "./dist/utils.spec.js"],
"console": "integratedTerminal",
"console": "internalConsole",
"internalConsoleOptions": "neverOpen"
}, {
"name": "Debug Device Tests",
Expand Down
4 changes: 2 additions & 2 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "roku-test-automation",
"version": "2.1.0-0",
"version": "2.2.0-3",
"description": "Helps with automating functional tests",
"main": "client/dist/index.js",
"typings": "client/dist/index.d.ts",
Expand Down
2 changes: 2 additions & 0 deletions client/rta-config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
"defaultBase": {
"description": "Allows specifying the default base that will be used if one was not provided in the args for a request",
"enum": [
"appUI",
"elementId",
"focusedNode",
"global",
"nodeRef",
Expand Down
152 changes: 124 additions & 28 deletions client/src/ECP.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ const sinon = sinonImport.createSandbox();
const expect = chai.expect;

import { ECP } from './ECP';
import * as utils from './test/utils';
import * as testUtils from './test/utils';
import type { ConfigOptions } from './types/ConfigOptions';
import type { AppUIResponse, AppUIResponseChild } from '.';

describe('ECP', function () {
let ecp: ECP;
Expand All @@ -27,15 +28,17 @@ describe('ECP', function () {
},
sendEcpGet: () => {
return ecpResponse;
}
},
setConfig: (config) => { }
};

config = {
RokuDevice: {
devices: []
}
};
ecp = new ECP(config);
ecp['device'] = device;

ecp = new ECP(device, config);
ecpUtils = ecp['utils'];
ecpResponse = '';
});
Expand All @@ -46,7 +49,7 @@ describe('ECP', function () {

describe('sendText', function () {
it('calls_device_sendEcpPost_for_each_character', async () => {
const stub = sinon.stub(device, 'sendEcpPost').callsFake(((path: string, params?: object, body?: needle.BodyData) => {}) as any);
const stub = sinon.stub(device, 'sendEcpPost').callsFake(((path: string, params?: object, body?: needle.BodyData) => { }) as any);

const text = 'love my life';
await ecp.sendText(text);
Expand All @@ -56,16 +59,16 @@ describe('ECP', function () {
it('uses_raspTemplateVariable_if_provided_instead_of_text_for_rasp_output', async () => {
ecp.startRaspFileCreation();
const raspFileSteps = (ecp as any).raspFileSteps as string[];
await ecp.sendText('[email protected]', {raspTemplateVariable: 'script-login'});
await ecp.sendText('[email protected]', { raspTemplateVariable: 'script-login' });
expect(raspFileSteps[0]).to.contain('script-login');
await ecp.sendText('123456', {raspTemplateVariable: 'script-password'});
await ecp.sendText('123456', { raspTemplateVariable: 'script-password' });
expect(raspFileSteps[1]).to.contain('script-password');
});
});

describe('sendKeypressSequence', function () {
it('calls_device_sendEcpPost_for_each_key', async () => {
const stub = sinon.stub(device, 'sendEcpPost').callsFake(((path: string, params?: object, body?: needle.BodyData) => {}) as any);
const stub = sinon.stub(device, 'sendEcpPost').callsFake(((path: string, params?: object, body?: needle.BodyData) => { }) as any);

const keys = [ECP.Key.Forward, ECP.Key.Play, ECP.Key.Rewind];
await ecp.sendKeypressSequence(keys);
Expand All @@ -85,16 +88,16 @@ describe('ECP', function () {
}
}) as any);

await ecp.sendKeypressSequence(keys, {count: count});
await ecp.sendKeypressSequence(keys, { count: count });
expect(stub.callCount).to.equal(keys.length * count);
});

it('should_not_send_any_keys_if_count_is_zero', async () => {
const keys = [ECP.Key.Forward, ECP.Key.Play, ECP.Key.Rewind];

const stub = sinon.stub(device, 'sendEcpPost').callsFake(((path: string, params?: object, body?: needle.BodyData) => {}) as any);
const stub = sinon.stub(device, 'sendEcpPost').callsFake(((path: string, params?: object, body?: needle.BodyData) => { }) as any);

await ecp.sendKeypressSequence(keys, {count: 0});
await ecp.sendKeypressSequence(keys, { count: 0 });
expect(stub.callCount).to.equal(0);
});
});
Expand All @@ -113,7 +116,7 @@ describe('ECP', function () {
});

it('does_not_sleep_if_not_requested', async () => {
const stub = sinon.stub(ecpUtils, 'sleep').callsFake(((milliseconds: number) => {}) as any);
const stub = sinon.stub(ecpUtils, 'sleep').callsFake(((milliseconds: number) => { }) as any);

await ecp.sendKeypress(ECP.Key.Home, 0);

Expand Down Expand Up @@ -248,7 +251,7 @@ describe('ECP', function () {

describe('getActiveApp', function () {
it('app_active', async () => {
ecpResponse = await utils.getNeedleMockResponse(this);
ecpResponse = await testUtils.getNeedleMockResponse(this);
const result = await ecp.getActiveApp();
expect(result.app?.id).to.equal('dev');
expect(result.app?.title).to.equal('mockAppTitle');
Expand All @@ -257,14 +260,14 @@ describe('ECP', function () {
});

it('no_app_or_screensaver_active', async () => {
ecpResponse = await utils.getNeedleMockResponse(this);
ecpResponse = await testUtils.getNeedleMockResponse(this);
const result = await ecp.getActiveApp();
expect(result.app?.id).to.not.be.ok;
expect(result.app?.title).to.equal('Roku');
});

it('screensaver_active_app_open', async () => {
ecpResponse = await utils.getNeedleMockResponse(this);
ecpResponse = await testUtils.getNeedleMockResponse(this);
const result = await ecp.getActiveApp();
expect(result.app?.id).to.equal('dev');
expect(result.app?.title).to.equal('mockAppTitle');
Expand All @@ -278,7 +281,7 @@ describe('ECP', function () {
});

it('screensaver_active_no_app_open', async () => {
ecpResponse = await utils.getNeedleMockResponse(this);
ecpResponse = await testUtils.getNeedleMockResponse(this);
const result = await ecp.getActiveApp();
expect(result.screensaver?.id).to.equal('261525');
expect(result.screensaver?.title).to.equal('Aquatic Life');
Expand All @@ -292,7 +295,7 @@ describe('ECP', function () {

describe('sendLaunchChannel', function () {
it('should_not_throw_if_successful_and_verification_is_enabled', async () => {
ecpResponse = await utils.getNeedleMockResponse(this);
ecpResponse = await testUtils.getNeedleMockResponse(this);
await ecp.sendLaunchChannel({
channelId: 'dev',
params: {},
Expand Down Expand Up @@ -329,7 +332,7 @@ describe('ECP', function () {
});

it('should_throw_if_launch_not_successful_and_verification_is_enabled', async () => {
sinon.stub(ecpUtils, 'sleep').callsFake(((milliseconds: number) => {}) as any);
sinon.stub(ecpUtils, 'sleep').callsFake(((milliseconds: number) => { }) as any);
try {
await ecp.sendLaunchChannel({
channelId: 'dev',
Expand All @@ -355,15 +358,15 @@ describe('ECP', function () {

describe('getMediaPlayer', function () {
it('app_closed', async () => {
ecpResponse = await utils.getNeedleMockResponse(this);
ecpResponse = await testUtils.getNeedleMockResponse(this);
const result = await ecp.getMediaPlayer();
expect(result.state).to.equal('close');
expect(result.error).to.equal(false);
expect(result.plugin).to.not.be.ok;
});

it('player_closed', async () => {
ecpResponse = await utils.getNeedleMockResponse(this);
ecpResponse = await testUtils.getNeedleMockResponse(this);
const result = await ecp.getMediaPlayer();

expect(result.state).to.equal('close');
Expand All @@ -375,7 +378,7 @@ describe('ECP', function () {
});

it('player_startup', async () => {
ecpResponse = await utils.getNeedleMockResponse(this);
ecpResponse = await testUtils.getNeedleMockResponse(this);
const result = await ecp.getMediaPlayer();

expect(result.state).to.equal('startup');
Expand All @@ -397,7 +400,7 @@ describe('ECP', function () {
});

it('player_buffering', async () => {
ecpResponse = await utils.getNeedleMockResponse(this);
ecpResponse = await testUtils.getNeedleMockResponse(this);
const result = await ecp.getMediaPlayer();

expect(result.state).to.equal('buffer');
Expand All @@ -419,7 +422,7 @@ describe('ECP', function () {
});

it('player_playing', async () => {
ecpResponse = await utils.getNeedleMockResponse(this);
ecpResponse = await testUtils.getNeedleMockResponse(this);
const result = await ecp.getMediaPlayer();

expect(result.state).to.equal('play');
Expand Down Expand Up @@ -447,7 +450,7 @@ describe('ECP', function () {
});

it('player_paused', async () => {
ecpResponse = await utils.getNeedleMockResponse(this);
ecpResponse = await testUtils.getNeedleMockResponse(this);
const result = await ecp.getMediaPlayer();

expect(result.state).to.equal('pause');
Expand Down Expand Up @@ -477,15 +480,15 @@ describe('ECP', function () {

describe('getChanperf', function () {
it('app_closed', async () => {
ecpResponse = await utils.getNeedleMockResponse(this);
ecpResponse = await testUtils.getNeedleMockResponse(this);
const result = await ecp.getChanperf();
expect(result.error).to.equal('Channel not running');
expect(result.status).to.equal('FAILED');
expect(result.plugin).to.not.be.ok;
});

it('app_open', async () => {
ecpResponse = await utils.getNeedleMockResponse(this);
ecpResponse = await testUtils.getNeedleMockResponse(this);
const result = await ecp.getChanperf();
expect(result.status).to.equal('OK');
expect(result.plugin?.id).to.equal('dev');
Expand All @@ -507,7 +510,7 @@ describe('ECP', function () {
const outputPath = 'test-path.rasp';

it('outputs_file_at_path_specified_with_correct_contents', async () => {
sinon.stub(ecpUtils, 'sleep').callsFake(((milliseconds: number) => {}) as any);
sinon.stub(ecpUtils, 'sleep').callsFake(((milliseconds: number) => { }) as any);
config.ECP = {
default: {
keypressDelay: 1500
Expand All @@ -527,7 +530,7 @@ describe('ECP', function () {
await ecp.sendKeypress(ecp.Key.Ok, 1);
ecp.finishRaspFileCreation(outputPath);
const expectedContents =
`params:
`params:
rasp_version: 1
default_keypress_wait: 1.5
steps:
Expand Down Expand Up @@ -555,4 +558,97 @@ steps:
fsExtra.removeSync(outputPath);
});
});

describe('getAppUI', function () {
let appUIResponse: AppUIResponse;

beforeEach(async () => {
ecpResponse = await testUtils.getNeedleMockResponse('ECP/getAppUI/appUIResponse');
});

it('Should add the sceneBoundingRects for scene', async () => {
appUIResponse = await ecp.getAppUI();

expect(appUIResponse.screen.children[0].sceneRect).to.haveOwnProperty('x');
expect(appUIResponse.screen.children[0].sceneRect).to.haveOwnProperty('y');
expect(appUIResponse.screen.children[0].sceneRect).to.haveOwnProperty('width');
expect(appUIResponse.screen.children[0].sceneRect).to.haveOwnProperty('height');
});

it('Should add the sceneBoundingRects for scene\'s children\'s children', async () => {
appUIResponse = await ecp.getAppUI();

for (const child of appUIResponse.screen?.children?.[0]?.children?.[4]?.children || []) {
expect(child.sceneRect).to.haveOwnProperty('x');
expect(child.sceneRect).to.haveOwnProperty('y');
expect(child.sceneRect).to.haveOwnProperty('width');
expect(child.sceneRect).to.haveOwnProperty('height');
}
});

it('should not return a key path for RowListItem or internal MarkupGrid but should still create the correct key path for its children', async () => {
appUIResponse = await ecp.getAppUI();
const row1 = appUIResponse.screen?.children?.[0]?.children?.[4]?.children?.[0]?.children?.[7]?.children?.[0];
expect(row1?.subtype).to.equal('RowListItem');
expect(row1?.keyPath).to.be.undefined;

const row1TitleComponent = row1?.children?.[0];
expect(row1TitleComponent?.subtype).to.equal('Label');
expect(row1TitleComponent?.keyPath).to.equal('#pagesContainerGroup.0.#rowListWithoutCustomTitleComponent.0.title');

const row1FirstItem = row1?.children?.[2]?.children?.[0];
expect(row1FirstItem?.subtype).to.equal('RowListItemComponent');
expect(row1FirstItem?.keyPath).to.equal('#pagesContainerGroup.0.#rowListWithoutCustomTitleComponent.0.items.0');
});

it('should return correct position for a node with offset bounds due to children', async () => {
appUIResponse = await ecp.getAppUI();
const rect3 = appUIResponse.screen?.children?.[0]?.children?.[4]?.children?.[0].children?.[2].children?.[0];
expect(rect3?.keyPath).to.equal('#pagesContainerGroup.0.#rect2.#rect3');
expect(rect3?.sceneRect?.height).to.equal(50);
expect(rect3?.sceneRect?.width).to.equal(50);
expect(rect3?.sceneRect?.x).to.equal(125);
expect(rect3?.sceneRect?.y).to.equal(125);
});

it('should return correct position for a node with offset bounds due to children', async () => {
appUIResponse = await ecp.getAppUI();
const offsetGroup = appUIResponse.screen?.children?.[0]?.children?.[4]?.children?.[0].children?.[3];
expect(offsetGroup?.keyPath).to.equal('#pagesContainerGroup.0.#offsetGroup');
expect(offsetGroup?.sceneRect?.height).to.equal(100);
expect(offsetGroup?.sceneRect?.width).to.equal(300);
expect(offsetGroup?.sceneRect?.x).to.equal(1600);
expect(offsetGroup?.sceneRect?.y).to.equal(200);
});

it('should return correct position for a RowList item', async () => {
appUIResponse = await ecp.getAppUI();
const rowListItemComponent0 = appUIResponse.screen?.children?.[0]?.children?.[4]?.children?.[0]?.children?.[7]?.children?.[0].children?.[2].children?.[0];
expect(rowListItemComponent0?.keyPath).to.equal('#pagesContainerGroup.0.#rowListWithoutCustomTitleComponent.0.items.0');
expect(rowListItemComponent0?.sceneRect?.height).to.equal(150);
expect(rowListItemComponent0?.sceneRect?.width).to.equal(300);
expect(rowListItemComponent0?.sceneRect?.x).to.equal(150);
expect(rowListItemComponent0?.sceneRect?.y).to.equal(336);
});

it('should return correct position for the second row RowList item', async () => {
appUIResponse = await ecp.getAppUI();
const rowListItemComponent1_0 = appUIResponse.screen?.children?.[0]?.children?.[4]?.children?.[0]?.children?.[7]?.children?.[1].children?.[2].children?.[0];
expect(rowListItemComponent1_0?.keyPath).to.equal('#pagesContainerGroup.0.#rowListWithoutCustomTitleComponent.1.items.0');
expect(rowListItemComponent1_0?.sceneRect?.height).to.equal(150);
expect(rowListItemComponent1_0?.sceneRect?.width).to.equal(300);
expect(rowListItemComponent1_0?.sceneRect?.x).to.equal(150);
expect(rowListItemComponent1_0?.sceneRect?.y).to.equal(536);
});

it('should return correct position for a custom row title element', async () => {
appUIResponse = await ecp.getAppUI();
const rowListCustomRowTitle0 = appUIResponse.screen?.children?.[0]?.children?.[4]?.children?.[0]?.children?.[8]?.children?.[0].children?.[0].children?.[0];
expect(rowListCustomRowTitle0?.keyPath).to.equal('#pagesContainerGroup.0.#rowListWithCustomTitleComponent.0.title');
expect(rowListCustomRowTitle0?.sceneRect?.height).to.equal(36);
expect(rowListCustomRowTitle0?.sceneRect?.width).to.equal(201);
expect(rowListCustomRowTitle0?.sceneRect?.x).to.equal(150);
expect(rowListCustomRowTitle0?.sceneRect?.y).to.equal(700);
});
});
});
Loading