blob: 5fb695f8e66aa373da36c06fd50ab8133897864a [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright 2017 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>tricium-client</title>
<script src="../bower_components/webcomponentsjs/webcomponents-lite.js"></script>
<script src="../bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../bower_components/iron-test-helpers/iron-test-helpers.html">
<link rel="import" href="../src/main/resources/static/tricium-client.html">
<test-fixture id="tricium-client-fixture">
<template>
<tricium-client></tricium-client>
</template>
</test-fixture>
<script>
async function assertRejects(promise, expectedReason = null) {
try {
await promise();
assert.fail('exception', 'no exception');
} catch (actualReason) {
if (expectedReason) {
assert.deepEqual(actualReason, expectedReason);
}
}
}
suite('<tricium-client> oauth-related behavior', () => {
let element;
let sandbox;
setup(() => {
element = fixture('tricium-client-fixture');
sandbox = sinon.sandbox.create();
sandbox.stub(window, 'gapi', {
auth: {
init: () => { throw Error('should be stubbed'); },
authorize: () => { throw Error('should be stubbed'); },
},
});
});
teardown(() => {
sandbox.restore();
});
function nowSeconds() {
return ~~(Date.now() / 1000);
}
test('_oauthTokenIsValid with valid token', () => {
assert.isTrue(element._oauthTokenIsValid({
access_token: 'foo',
expires_at: (nowSeconds() + 3600) + '',
}));
});
test('_oauthTokenIsValid with no access_token field', () => {
assert.isFalse(element._oauthTokenIsValid({
expires_at: (nowSeconds() + 3600) + '',
}));
});
test('_oauthTokenIsValid with expired token', () => {
assert.isFalse(element._oauthTokenIsValid({
access_token: 'foo',
expires_at: (nowSeconds() - 5) + '',
}));
});
test('_oauthTokenIsValid with invalid values', () => {
assert.isFalse(element._oauthTokenIsValid(null));
assert.isFalse(element._oauthTokenIsValid(undefined));
assert.isFalse(element._oauthTokenIsValid({access_token: 'foo'}));
});
test('_refreshToken resolves when authorize gives token', async () => {
const token = {
access_token: 'test_access_token',
expires_at: (nowSeconds() + 3600) + '',
};
element._sharedAuthState = {
config: {
client_id: 'test_client_id',
email: 'johndoe@example.com',
},
};
sandbox.stub(window.gapi.auth, 'authorize', (opts, callback) => {
callback(token);
});
const value = await element._refreshToken(100);
assert.deepEqual(value, token);
});
test('_refreshToken rejects when authorize gives error', async () => {
const token = {error: 'could not authorize'};
sandbox.stub(window.gapi.auth, 'authorize', (opts, callback) => {
callback(token);
});
assertRejects(async () => await element._refreshToken(100));
});
test('_refreshToken rejects when authorize times out', async () => {
// In this test, we make it so that authorize never resolves,
// and the timeout is made smaller so that the test completes faster.
sandbox.stub(window.gapi.auth, 'authorize', () => new Promise(() => {}));
assertRejects(async () => await element._refreshToken(0));
});
});
suite('<tricium-client> rpc request behavior', () => {
let element;
const change = {
id: 'myProject~master~I3ea943139cb62e86071996f2480e58bf3eeb9dd2',
project: 'myProject',
branch: 'master',
subject: 'Implement Feature X',
};
const revision = {
kind: 'REWORK',
ref: 'refs/changes/99/4799/2',
subject: 'My Patchset',
};
const comment = {
id: '8a0b9120_a01440e3',
robot_id: 'Hello',
robot_run_id: 123412341234,
properties: {
tricium_comment_uuid: 'f37e05e3-ce3b-475b-a4cd-2cc60814bc69',
},
};
function newSuccessResponse(data) {
return new window.Response(')]}\'' + JSON.stringify(data), {
status: 200,
headers: {
'Content-Type': 'application/json',
'X-Prpc-Grpc-Code': '0',
},
});
}
setup(() => {
element = fixture('tricium-client-fixture');
element.triciumHost = 'tricium-mock.appspot.com';
sinon.stub(window, 'fetch');
});
teardown(() => {
window.fetch.restore();
});
test('getProgress with a success response', async () => {
const responseData = {
runId: '56511',
state: 'SUCCESS',
functionProgress: [
{
name: 'GitFileIsolator',
state: 'SUCCESS',
swarmingUrl: 'https://swarm.example.com',
swarmingTaskId: '390a',
},
{
name: 'Spacey',
state: 'SUCCESS',
swarmingUrl: 'https://swarm.example.com',
swarmingTaskId: '390b',
numComments: 2,
},
],
};
const response = newSuccessResponse(responseData);
window.fetch.returns(Promise.resolve(response));
const data = await element.getProgress(change, revision);
assert.deepEqual(data, responseData);
});
test('getProgress with invalid argument response', async () => {
const response = new window.Response('Invalid Argument', {
status: 400,
headers: {'X-Prpc-Grpc-Code': '3'},
});
window.fetch.returns(Promise.resolve(response));
assertRejects(
async () => element.getProgress(change, revision),
'X-Prpc-Grpc-Code: 3\nHTTP status: 400\nBody: Invalid Argument');
});
test('getProgress with not found response', async () => {
// If no progress was found, we don't necessarily want
// to treat this as an error case because it may just
// not be finished yet.
const response = new window.Response('Not found', {
status: 404,
headers: {'X-Prpc-Grpc-Code': '5'},
});
window.fetch.returns(Promise.resolve(response));
const data = await element.getProgress(change, revision);
assert.isNull(data);
});
test('getProgress with invalid response', async () => {
const response = new window.Response('No pRPC code', {
status: 200,
});
window.fetch.returns(Promise.resolve(response));
assertRejects(
async () => await element.getProgress(change, revision),
'X-Prpc-Grpc-Code: NaN\nHTTP status: 200\nBody: No pRPC code');
});
test('reportNotUseful with valid comment', async () => {
const responseData = {responseField: 123};
const response = newSuccessResponse(responseData);
window.fetch.returns(Promise.resolve(response));
const data = await element.reportNotUseful(comment);
assert.deepEqual(data, responseData);
});
test('reportNotUseful with invalid empty comment', async () => {
const response = newSuccessResponse({responseField: 123});
window.fetch.returns(Promise.resolve(response));
assertRejects(
async () => await element.reportNotUseful({properties: {}}),
'No tricium_comment_uuid field in comment.properties: {}');
});
test('_message describes response contents', async () => {
const successResponse = newSuccessResponse({responseField: 123});
const message = await element._message(successResponse);
assert.equal(
message,
'X-Prpc-Grpc-Code: 0\nHTTP status: 200\n' +
'Body: )]}\'{"responseField":123}');
});
test('_parseRpcJson parses pRPC JSON', async () => {
const response = newSuccessResponse({mydata: 42});
const data = await element._parseRpcJson(response);
assert.deepEqual(data, {mydata: 42});
});
test('_parseRpcJson raises with invalid JSON', async () => {
const response = new window.Response('not valid JSON', {
status: 500,
headers: {'X-Prpc-Grpc-Code': '1'},
});
assertRejects(async () => await element._parseRpcJson(response));
});
test('_rpcCode with an error code present', () => {
const response = new window.Response('Invalid Argument', {
status: 400,
headers: {'X-Prpc-Grpc-Code': '3'},
});
assert.equal(element._rpcCode(response), 3);
});
test('_rpcCode with missing header', () => {
const response = new window.Response('Not pRPC', {
status: 200,
});
assert.isNaN(element._rpcCode(response));
});
});
</script>