Skip to content

Commit

Permalink
API Integration Tests via jest (grafana#10899)
Browse files Browse the repository at this point in the history
* tests: experiment with api tests

* api tests are getting nice

* api: api testing ready for feedback
  • Loading branch information
torkelo authored Feb 14, 2018
1 parent bb681cd commit ead1c30
Show file tree
Hide file tree
Showing 11 changed files with 373 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .jshintrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"bitwise":false,
"curly": true,
"eqnull": true,
"strict": true,
"strict": false,
"devel": true,
"eqeqeq": true,
"forin": false,
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"angular-mocks": "^1.6.6",
"autoprefixer": "^6.4.0",
"awesome-typescript-loader": "^3.2.3",
"axios": "^0.17.1",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-es2015": "^6.24.1",
Expand Down Expand Up @@ -105,6 +106,7 @@
"lint": "tslint -c tslint.json --project tsconfig.json --type-check",
"karma": "node ./node_modules/grunt-cli/bin/grunt karma:dev",
"jest": "node ./node_modules/jest-cli/bin/jest.js --notify --watch",
"api-tests": "node ./node_modules/jest-cli/bin/jest.js --notify --watch --config=tests/api/jest.js",
"precommit": "lint-staged && node ./node_modules/grunt-cli/bin/grunt precommit"
},
"lint-staged": {
Expand Down
7 changes: 7 additions & 0 deletions tests/api/clearState.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import * as setup from './setup';

describe.skip('clear state', () => {
it('will clear state', () => {
return setup.clearState();
});
});
30 changes: 30 additions & 0 deletions tests/api/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const axios = require('axios');

export function getClient(options) {
return axios.create({
baseURL: 'http://localhost:3000',
timeout: 1000,
auth: {
username: options.username,
password: options.password,
},
});
}

export function getAdminClient() {
return getClient({
username: 'admin',
password: 'admin',
});
}

let client = getAdminClient();

client.callAs = function(user) {
return getClient({
username: user.login,
password: 'password',
});
};

export default client;
45 changes: 45 additions & 0 deletions tests/api/dashboard.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import client from './client';
import * as setup from './setup';

describe('/api/dashboards', () => {
let state: any = {};

beforeAll(async () => {
state = await setup.ensureState({
orgName: 'api-test-org',
users: [
{ user: setup.admin, role: 'Admin' },
{ user: setup.editor, role: 'Editor' },
{ user: setup.viewer, role: 'Viewer' },
],
admin: setup.admin,
dashboards: [
{
title: 'aaa',
uid: 'aaa',
},
{
title: 'bbb',
uid: 'bbb',
},
],
});
});

describe('With admin user', () => {
it('can delete dashboard', async () => {
let rsp = await client.callAs(setup.admin).delete(`/api/dashboards/uid/aaa`);
expect(rsp.data.title).toBe('aaa');
});
});

describe('With viewer user', () => {
it('Cannot delete dashboard', async () => {
let rsp = await setup.expectError(() => {
return client.callAs(setup.viewer).delete(`/api/dashboards/uid/bbb`);
});

expect(rsp.response.status).toBe(403);
});
});
});
19 changes: 19 additions & 0 deletions tests/api/jest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module.exports = {
verbose: true,
"globals": {
"ts-jest": {
"tsConfigFile": "tsconfig.json"
}
},
"transform": {
"^.+\\.tsx?$": "<rootDir>/../../node_modules/ts-jest/preprocessor.js"
},
"moduleDirectories": ["node_modules"],
"testRegex": "(\\.|/)(test)\\.ts$",
"testEnvironment": "node",
"moduleFileExtensions": [
"ts",
"js",
"json"
],
};
27 changes: 27 additions & 0 deletions tests/api/search.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import client from './client';
import * as setup from './setup';

describe('GET /api/search', () => {
const state = {};

beforeAll(async () => {
state = await setup.ensureState({
orgName: 'api-test-org',
users: [{ user: setup.admin, role: 'Admin' }],
admin: setup.admin,
dashboards: [
{
title: 'Dashboard in root no permissions',
uid: 'AAA',
},
],
});
});

describe('With admin user', () => {
it('should return all dashboards', async () => {
let rsp = await client.callAs(state.admin).get('/api/search');
expect(rsp.data).toHaveLength(1);
});
});
});
107 changes: 107 additions & 0 deletions tests/api/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import client from './client';
import _ from 'lodash;';

export const editor = {
email: '[email protected]',
login: 'api-test-editor',
password: 'password',
name: 'Api Test Editor',
};

export const admin = {
email: '[email protected]',
login: 'api-test-admin',
password: 'password',
name: 'Api Test Super',
};

export const viewer = {
email: '[email protected]',
login: 'api-test-viewer',
password: 'password',
name: 'Api Test Viewer',
};

export async function expectError(callback) {
try {
let rsp = await callback();
return rsp;
} catch (err) {
return err;
}

return rsp;
}

// deletes org if it's already there
export async function getOrg(orgName) {
try {
const rsp = await client.get(`/api/orgs/name/${orgName}`);
await client.delete(`/api/orgs/${rsp.data.id}`);
} catch {}

const rsp = await client.post(`/api/orgs`, { name: orgName });
return { name: orgName, id: rsp.data.orgId };
}

export async function getUser(user) {
const search = await client.get('/api/users/search', {
params: { query: user.login },
});

if (search.data.totalCount === 1) {
user.id = search.data.users[0].id;
return user;
}

const rsp = await client.post('/api/admin/users', user);
user.id = rsp.data.id;

return user;
}

export async function addUserToOrg(org, user, role) {
const rsp = await client.post(`/api/orgs/${org.id}/users`, {
loginOrEmail: user.login,
role: role,
});

return rsp.data;
}

export async function clearState() {
const admin = await getUser(adminUser);
const rsp = await client.delete(`/api/admin/users/${admin.id}`);
return rsp.data;
}

export async function setUsingOrg(user, org) {
await client.callAs(user).post(`/api/user/using/${org.id}`);
}

export async function createDashboard(user, dashboard) {
const rsp = await client.callAs(user).post(`/api/dashboards/db`, {
dashboard: dashboard,
overwrite: true,
});
dashboard.id = rsp.data.id;
dashboard.url = rsp.data.url;

return dashboard;
}

export async function ensureState(state) {
const org = await getOrg(state.orgName);

for (let orgUser of state.users) {
const user = await getUser(orgUser.user);
await addUserToOrg(org, user, orgUser.role);
await setUsingOrg(user, org);
}

for (let dashboard of state.dashboards) {
await createDashboard(state.admin, dashboard);
}

return state;
}
24 changes: 24 additions & 0 deletions tests/api/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"compilerOptions": {
"moduleResolution": "node",
"target": "es6",
"lib": ["es6"],
"module": "commonjs",
"declaration": false,
"allowSyntheticDefaultImports": true,
"inlineSourceMap": false,
"sourceMap": true,
"noEmitOnError": false,
"emitDecoratorMetadata": false,
"experimentalDecorators": true,
"noImplicitReturns": true,
"noImplicitThis": false,
"noImplicitUseStrict":false,
"noImplicitAny": false,
"noUnusedLocals": true
},
"include": [
"*.ts",
"**/*.ts"
]
}
22 changes: 22 additions & 0 deletions tests/api/user.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import client from './client';
import * as setup from './setup';

describe('GET /api/user', () => {
it('should return current authed user', async () => {
let rsp = await client.get('/api/user');
expect(rsp.data.login).toBe('admin');
});
});

describe('PUT /api/user', () => {
it('should update current authed user', async () => {
const user = await setup.getUser(setup.editor);
user.name = 'Updated via test';

const rsp = await client.callAs(user).put('/api/user', user);
expect(rsp.data.message).toBe('User updated');

const updated = await client.callAs(user).get('/api/user');
expect(updated.data.name).toBe('Updated via test');
});
});
Loading

0 comments on commit ead1c30

Please sign in to comment.