Skip to content

Commit

Permalink
Add unit tests for telemetry module
Browse files Browse the repository at this point in the history
  • Loading branch information
raphinesse committed Oct 16, 2019
1 parent 1a2ee93 commit 3df0871
Show file tree
Hide file tree
Showing 2 changed files with 279 additions and 0 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-standard": "^4.0.0",
"jasmine": "^3.3.1",
"mock-stdin": "^0.3.1",
"nyc": "^14.1.1",
"rewire": "^4.0.1"
},
Expand Down
278 changes: 278 additions & 0 deletions spec/telemetry.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
/*!
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

const rewire = require('rewire');
const Insight = require('insight');
const mockStdin = require('mock-stdin');

describe('telemetry', () => {
let telemetry, insight;

beforeEach(() => {
telemetry = rewire('../src/telemetry');
insight = telemetry.__get__('insight');

// Prevent any settings from being persisted during testing
insight.config = {
get: jasmine.createSpy('insight.config.get'),
set: jasmine.createSpy('insight.config.set')
};

// Prevent tracking anything during testing
spyOn(Insight.prototype, '_save');

// Prevent prompts during testing
spyOn(Insight.prototype, 'askPermission');
});

describe('hasUserOptedInOrOut', () => {
it('is false if insight.optOut is unset [T001]', () => {
expect(telemetry.hasUserOptedInOrOut()).toBe(false);
expect(insight.config.get).toHaveBeenCalledWith('optOut');
});

it('is true if insight.optOut is set [T002]', () => {
insight.config.get.and.returnValues(
false, true, 0, 1, '', 'xxx', null
);
for (let i = 0; i < 7; i++) {
expect(telemetry.hasUserOptedInOrOut()).toBe(true);
}
expect(insight.config.get).toHaveBeenCalledTimes(7);
});
});

describe('isOptedIn', () => {
it('is the inverse of insight.optOut [T003]', () => {
insight.config.get.and.returnValues(false, true);

expect(telemetry.isOptedIn()).toBe(true);
expect(telemetry.isOptedIn()).toBe(false);
expect(insight.config.get).toHaveBeenCalledTimes(2);
});

it('is true if user did not yet decide [T004]', () => {
expect(telemetry.isOptedIn()).toBe(true);
expect(insight.config.get).toHaveBeenCalledWith('optOut');
});
});

describe('clear', () => {
it('clears telemetry setting [T005]', () => {
telemetry.clear();
expect(insight.config.set)
.toHaveBeenCalledWith('optOut', undefined);
});
});

describe('turnOn', () => {
it('enables the telemetry setting [T006]', () => {
telemetry.turnOn();
expect(insight.config.set)
.toHaveBeenCalledWith('optOut', false);
});
});

describe('turnOff', () => {
it('disables the telemetry setting [T007]', () => {
telemetry.turnOff();
expect(insight.config.set)
.toHaveBeenCalledWith('optOut', true);
});
});

describe('track', () => {
beforeEach(() => {
spyOn(Insight.prototype, 'track');
});

it('calls insight.track [T008]', () => {
telemetry.track();
expect(insight.track).toHaveBeenCalled();
});

it('passes its arguments to insight.track [T009]', () => {
const args = ['foo', 'bar', 42];
telemetry.track(...args);
expect(insight.track).toHaveBeenCalledWith(...args);
});

// FIXME filtering is currently broken
xit('filters falsy and empty arguments [T010]', () => {
const args = [null, [23], [], 42, ''];
telemetry.track(...args);
expect(insight.track).toHaveBeenCalledWith([23], 42);
});
});

describe('showPrompt', () => {
let response;

beforeEach(() => {
spyOn(console, 'log');
spyOn(telemetry, 'track');
response = Symbol('response');
insight.askPermission.and.callFake((_, cb) => cb(null, response));
});

it('calls insight.askPermission [T011]', () => {
return telemetry.showPrompt().then(_ => {
expect(insight.askPermission).toHaveBeenCalled();
});
});

it('returns a promise resolved to the user response [T012]', () => {
return telemetry.showPrompt().then(result => {
expect(result).toBe(response);
});
});

describe('when user opts in', () => {
beforeEach(() => {
response = true;
});

it('thanks the user [T013]', () => {
return telemetry.showPrompt().then(_ => {
expect(console.log).toHaveBeenCalledWith(
jasmine.stringMatching(/thanks/i)
);
});
});

it('tracks the user decision [T014]', () => {
return telemetry.showPrompt().then(_ => {
expect(telemetry.track).toHaveBeenCalledWith(
'telemetry', 'on', 'via-cli-prompt-choice', 'successful'
);
});
});
});

describe('when user declines', () => {
beforeEach(() => {
response = false;
});

it('returns a resolved promise if the user response was negative [T015]', () => {
return telemetry.showPrompt().then(result => {
expect(result).toBe(false);
});
});

it('informs the user [T016]', () => {
return telemetry.showPrompt().then(_ => {
expect(console.log).toHaveBeenCalledWith(
jasmine.stringMatching(/opted out of telemetry.* cordova telemetry on/i)
);
});
});

it('tracks the user decision [T017]', () => {
return telemetry.showPrompt().then(_ => {
expect(telemetry.track).toHaveBeenCalledWith(
'telemetry', 'off', 'via-cli-prompt-choice', 'successful'
);
});
});
});

describe('gory details', () => {
let CI, stdin;
beforeEach(() => {
CI = process.env.CI;
delete process.env.CI;
stdin = mockStdin.stdin();
insight.askPermission.and.callThrough();

// To silence the prompts by insight
spyOn(process.stdout, 'write');
});
afterEach(() => {
stdin.restore();
if (CI !== undefined) {
process.env.CI = CI;
}
});

it('saves the user response [T018]', () => {
process.nextTick(_ => stdin.send('y\n'));
return telemetry.showPrompt().then(result => {
expect(result).toBe(true);
expect(insight.config.set)
.toHaveBeenCalledWith('optOut', false);
});
});

it('is counted as a negative response if user does not decide [T019]', () => {
telemetry.timeoutInSecs = 0.01;
return telemetry.showPrompt().then(result => {
expect(result).toBe(false);
expect(insight.config.set)
.toHaveBeenCalledWith('optOut', true);
});
});

it('does NOT show prompt when running on a CI [T020]', () => {
process.env.CI = 1;
return telemetry.showPrompt().then(result => {
expect(result).toBe(false);
expect(insight.config.set).not.toHaveBeenCalled();
expect(process.stdout.write).not.toHaveBeenCalled();
});
});
});
});
describe('insight.track', () => {
it('tracks without user choice [T021]', () => {
insight.track();
expect(insight._save).toHaveBeenCalled();
});

it('tracks with user consent [T022]', () => {
insight.config.get.and.returnValue(false);
insight.track();
expect(insight._save).toHaveBeenCalled();
});

it('does NOT track when user opted out [T023]', () => {
insight.config.get.and.returnValue(true);
insight.track();
expect(insight._save).not.toHaveBeenCalled();
});

describe('on CI', () => {
let CI;
beforeEach(() => {
CI = process.env.CI;
process.env.CI = 1;
});
afterEach(() => {
if (CI !== undefined) {
process.env.CI = CI;
}
});

it('does still track [T024]', () => {
insight.track();
expect(insight._save).toHaveBeenCalled();
});
});
});
});

0 comments on commit 3df0871

Please # to comment.