Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
15 changes: 8 additions & 7 deletions src/Errors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { RokuMessages } from './RokuDeploy';
import type { HttpResponse, RokuMessages } from './RokuDeploy';

export class InvalidDeviceResponseCodeError extends Error {
constructor(message: string, public results?: any) {
Expand Down Expand Up @@ -57,15 +57,16 @@ export class MissingRequiredOptionError extends Error {
}

export class UpdateCheckRequiredError extends Error {
results: any;

cause: Error;
static MESSAGE = `Your device needs to check for updates before accepting connections. Please navigate to System Settings and check for updates and then try again.\n\nhttps://support.roku.com/article/208755668.`;

constructor(originalError: Error) {
constructor(response: HttpResponse) {
super();
this.message = `Your device needs to check for updates before accepting connections. Please navigate to System Settings and check for updates and then try again.\n\nhttps://support.roku.com/article/208755668.`;
this.results = { response: { statusCode: 577 } };
this.cause = originalError;
this.message = UpdateCheckRequiredError.MESSAGE;
//this exact structure helps `roku-debug` detect this error by finding this status code and then showing a nice popup
this.results = { response: { ...response ?? {}, statusCode: 500 } };
Object.setPrototypeOf(this, UpdateCheckRequiredError.prototype);
}

results: any;
}
33 changes: 25 additions & 8 deletions src/RokuDeploy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1103,17 +1103,18 @@ describe('index', () => {
});
});

it('rejects when response contains invalid password status code', () => {
it('rejects when response contains invalid password status code', async () => {
options.failOnCompileError = true;
mockDoPostRequest('', 577);
mockDoPostRequest(`'Failed to check for software update'`, 200);

return rokuDeploy.publish(options).then(() => {
try {
await rokuDeploy.publish(options);
assert.fail('Should not have succeeded due to roku server compilation failure');
}, (err) => {
expect(err.message).to.be.a('string').and.satisfy(msg => msg.startsWith(`Your device needs to check for updates before accepting connections. Please navigate to System Settings and check for updates and then try again.

https://support.roku.com/article/208755668.`));
});
} catch (err) {
expect((err as any).message).to.eql(
errors.UpdateCheckRequiredError.MESSAGE
);
}
});

it('handles successful deploy', () => {
Expand Down Expand Up @@ -3642,6 +3643,22 @@ https://support.roku.com/article/208755668.`));
});
});

describe('isUpdateCheckRequiredResponse', () => {
it('matches on actual response from device', () => {
const response = `<html>\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"HandheldFriendly\" content=\"True\">\n <title> Roku Development Kit </title>\n\n <link rel=\"stylesheet\" type=\"text/css\" media=\"screen\" href=\"css/global.css\" />\n</head>\n<body>\n <div id=\"root\" style=\"background: #fff\">\n\n </div>\n\n <script type=\"text/javascript\" src=\"css/global.js\"></script>\n <script type=\"text/javascript\">\n \n // Include core components and resounce bundle (needed)\n Shell.resource.set(null, {\n endpoints: {} \n });\n Shell.create('Roku.Event.Key');\n Shell.create('Roku.Events.Resize');\n Shell.create('Roku.Events.Scroll'); \n // Create global navigation and render it\n var nav = Shell.create('Roku.Nav')\n .trigger('Enable standalone and utility mode - hide user menu, shopping cart, and etc.')\n .trigger('Use compact footer')\n .trigger('Hide footer')\n .trigger('Render', document.getElementById('root'))\n .trigger('Remove all feature links from header')\n\n // Retrieve main content body node\n var node = nav.invoke('Get main body section mounting node');\n \n // Create page container and page header\n var container = Shell.create('Roku.Nav.Page.Standard').trigger('Render', node);\n node = container.invoke('Get main body node');\n container.invoke('Get headline node').innerHTML = 'Failed to check for software update';\n\t // Cannot reach Software Update Server\n node.innerHTML = '<p>Please make sure that your Roku device is connected to internet and running most recent software.</p> <p> After connecting to internet, go to system settings and check for software update.</p> ';\n\n var hrDiv = document.createElement('div');\n hrDiv.innerHTML = '<hr />';\n node.appendChild(hrDiv);\n\n var d = document.createElement('div');\n d.innerHTML = '<br />';\n node.appendChild(d);\n\n </script>\n\n\n <div style=\"display:none\">\n\n <font color=\"red\">Please make sure that your Roku device is connected to internet, and running most recent software version (d=953108)</font>\n\n </div>\n\n</body>\n</html>\n`;
expect(
rokuDeploy['isUpdateCheckRequiredResponse'](response)
).to.be.true;
});

it('matches with some variations to the message', () => {
const response = `" FAILED tocheck\tfor softwareupdate"`;
expect(
rokuDeploy['isUpdateCheckRequiredResponse'](response)
).to.be.true;
});
});

function mockDoGetRequest(body = '', statusCode = 200) {
return sinon.stub(rokuDeploy as any, 'doGetRequest').callsFake((params) => {
let results = { response: { statusCode: statusCode }, body: body };
Expand Down
24 changes: 14 additions & 10 deletions src/RokuDeploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -468,19 +468,16 @@ export class RokuDeploy {
if (this.isCompileError(replaceError.message) && options.failOnCompileError) {
throw new errors.CompileError('Compile error', replaceError, replaceError.results);
} else {
try {
response = await this.doPostRequest(requestOptions);
} catch (installError: any) {
switch (installError.results.response.statusCode) {
case 577:
throw new errors.UpdateCheckRequiredError(installError);
default:
throw installError;
}
}
requestOptions.formData.mysubmit = 'Install';
response = await this.doPostRequest(requestOptions);
}
}

//if we got a non-error status code, but the body includes a message about needing to update, throw a special error
if (this.isUpdateCheckRequiredResponse(response.body)) {
throw new errors.UpdateCheckRequiredError(response);
}

if (options.failOnCompileError) {
if (this.isCompileError(response.body)) {
throw new errors.CompileError('Compile error', response, this.getRokuMessagesFromResponseBody(response.body));
Expand Down Expand Up @@ -512,6 +509,13 @@ export class RokuDeploy {
return !!/install\sfailure:\scompilation\sfailed/i.exec(responseHtml);
}

/**
* Does the response look like a compile error
*/
private isUpdateCheckRequiredResponse(responseHtml: string) {
return !!/["']\s*Failed\s*to\s*check\s*for\s*software\s*update\s*["']/i.exec(responseHtml);
}

/**
* Converts existing loaded package to squashfs for faster loading packages
* @param options
Expand Down