Skip to content
This repository has been archived by the owner on Apr 17, 2023. It is now read-only.

Commit

Permalink
js: added unit tests for vue components and utils
Browse files Browse the repository at this point in the history
  • Loading branch information
vitoravelino committed Feb 6, 2018
1 parent 98d86aa commit ecca2d9
Show file tree
Hide file tree
Showing 17 changed files with 609 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"presets": [
["es2015", { "modules": false }]
"es2015"
],
"plugins": [
"lodash",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@
methods: {
copyToClipboard() {
const $temp = $('<input>');
const tempInput = document.createElement('input');
$('body').append($temp);
$temp.val(this.commandToPull).select();
document.body.appendChild(tempInput);
tempInput.value = this.commandToPull;
tempInput.select();
document.execCommand('copy');
$temp.remove();
this.$alert.$show('Copied pull command to clipboard');
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

<td>{{ tag[0].author.name }}</td>

<td>
<td class="image-id">
<span v-if="tag[0].image_id === ''">-</span>
<span v-else :title="prettyFormatID">
{{ shortFormatID }}
Expand All @@ -24,7 +24,7 @@

<td>{{ tag[0].updated_at }}</td>

<td v-if="securityEnabled">
<td class="vulns" v-if="securityEnabled">
<span v-if="scanPending">Pending</span>
<span v-if="scanInProgress">In progress</span>
<a :href="tagLink" v-if="scanDone">
Expand Down
13 changes: 7 additions & 6 deletions app/assets/javascripts/shared/components/table-pagination.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@
</div>
<div class="col-sm-6 text-right" v-if="totalPages > 1">
<ul class="pagination">
<li :class="{ 'disabled': previousDisabled }">
<li class="previous" :class="{ 'disabled': previousDisabled }">
<a href="#" @click.prevent="setCurrentPage(currentPage - 1)">Previous</a>
</li>
<li
class="page"
v-for="(page, index) in displayedPages"
:key="index"
:class="{ 'active': currentPage == page }">
<a href="#" @click.prevent="setCurrentPage(page)">{{ page }}</a>
</li>
<li :class="{ 'disabled': nextDisabled }">
<li class="next" :class="{ 'disabled': nextDisabled }">
<a href="#" @click.prevent="setCurrentPage(currentPage + 1)">Next</a>
</li>
</ul>
Expand All @@ -29,10 +30,6 @@
import range from '~/utils/range';
export default {
template: `
`,
props: ['total', 'itensPerPage', 'currentPage'],
data() {
Expand All @@ -57,6 +54,10 @@
},
displayedPages() {
if (this.totalPages === 0) {
return [];
}
let minRange = this.currentPage - this.showMore;
let maxRange = this.currentPage + this.showMore;
Expand Down
10 changes: 7 additions & 3 deletions app/assets/javascripts/utils/range.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
// generates a range of integers
export default (start, end) => (
Array.from({ length: (end - start) + 1 }, (_, i) => i + start)
);
export default (start, end) => {
if (start > end) {
throw new Error('Range: "start" cannot be greater than "end"');
}

return Array.from({ length: (end - start) + 1 }, (_, i) => i + start);
};
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"scripts": {
"eslint": "eslint --max-warnings 0 --ext .js,.vue ./app/assets/javascripts",
"webpack": "webpack --watch --config config/webpack.js",
"test": "cross-env NODE_ENV=test mocha-webpack --webpack-config ./config/webpack.js --require ./spec/javascripts/setup.js ./spec/javascripts/**/*.spec.js",
"test": "cross-env NODE_ENV=test mocha-webpack --webpack-config ./config/webpack.js --require ./spec/javascripts/setup.js ./spec/javascripts/**/*.spec.js ./spec/javascripts/**/**/*.spec.js",
"cover": "cross-env NODE_ENV=test nyc --reporter=text --reporter=html yarn test"
},
"nyc": {
Expand Down Expand Up @@ -32,6 +32,7 @@
"mocha": "^4.1.0",
"mocha-webpack": "^1.0.1",
"nyc": "^11.4.1",
"sinon": "^4.2.2",
"vue-test-utils": "^1.0.0-beta.9",
"webpack-dev-server": "^2.3.0",
"webpack-node-externals": "^1.6.0"
Expand Down
51 changes: 51 additions & 0 deletions spec/javascripts/modules/repositories/delete-tag-action.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { mount } from 'vue-test-utils';

import DeleteTagAction from '~/modules/repositories/components/delete-tag-action';

import sinon from 'sinon';

describe('delete-tag-action', () => {
let wrapper;

beforeEach(() => {
wrapper = mount(DeleteTagAction, {
propsData: {
state: {
selectedTags: [],
},
},
mocks: {
$bus: {
$emit: sinon.spy(),
},
},
});
});

it('shows nothing if no tag is selected', () => {
expect(wrapper.find('.label').exists()).toBe(false);
});

it('shows label in singular if only one tag is selected', () => {
wrapper.setProps({ state: { selectedTags: [{}] } });

expect(wrapper.text()).toBe('Delete tag');
});

it('shows label in plural if more than one tag is selected', () => {
wrapper.setProps({ state: { selectedTags: [{}, {}] } });
expect(wrapper.text()).toBe('Delete tags');
});

it('shows label in plural if more a tag with multiple labels is selected', () => {
wrapper.setProps({ state: { selectedTags: [{ multiple: true }] } });
expect(wrapper.text()).toBe('Delete tags');
});

it('emits deleteTags event if clicked', () => {
wrapper.setProps({ state: { selectedTags: [{}] } });

wrapper.find('.btn').trigger('click');
expect(wrapper.vm.$bus.$emit.calledWith('deleteTags')).toBe(true);
});
});
53 changes: 53 additions & 0 deletions spec/javascripts/modules/repositories/tag.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { mount } from 'vue-test-utils';

import Tag from '~/modules/repositories/components/tag';

import sinon from 'sinon';

describe('tag', () => {
let wrapper;
const commandToPull = 'docker pull localhost:5000/opensuse/portus:latest';

beforeEach(() => {
wrapper = mount(Tag, {
propsData: {
// not real data but the one used in the component
tag: {
name: 'latest',
},
repository: {
registry_hostname: 'localhost:5000',
full_name: 'opensuse/portus',
},
},
mocks: {
$alert: {
$show: sinon.spy(),
},
},
});
});

it('shows tag name', () => {
expect(wrapper.find('.label').text()).toBe('latest');
});

it('computes command to pull properly', () => {
expect(wrapper.vm.commandToPull).toBe(commandToPull);
});

it('copies command to pull to the clipboard', () => {
// document in test env doesn't have execCommand
document.execCommand = sinon.spy();

wrapper.find('.label').trigger('click');
expect(document.execCommand.calledWith('copy')).toBe(true);
});

it('calls $alert plugin notifying user', () => {
const message = 'Copied pull command to clipboard';

wrapper.find('.label').trigger('click');
expect(wrapper.vm.$alert.$show.calledWith(message)).toBe(true);
});
});
84 changes: 84 additions & 0 deletions spec/javascripts/modules/repositories/tags-table-row.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { mount } from 'vue-test-utils';

import TagsTableRow from '~/modules/repositories/components/tags-table-row';

import sinon from 'sinon';

describe('tags-table-row', () => {
let wrapper;
const tag = [
{
id: 1,
name: 'latest',
author: {
id: 2,
name: 'vitoravelino',
},
digest: 'sha256:4cee1979ba0bf7db9fc5d28fb7b798ca69ae95a47c5fecf46327720df4ff352d',
image_id: '5b0d59026729b68570d99bc4f3f7c31a2e4f2a5736435641565d93e7c25bd2c3',
created_at: '2018-02-06T12:54:31.000Z',
updated_at: '2018-02-06T12:54:31.000Z',
scanned: 2,
vulnerabilities: {
clair: [],
},
},
];

beforeEach(() => {
wrapper = mount(TagsTableRow, {
propsData: {
state: {
selectedTags: [],
repository: {},
},
tag,
canDestroy: true,
securityEnabled: true,
},
mocks: {
$bus: {
$emit: sinon.spy(),
},
},
});
});

it('shows checkbox for selection if able to destroy', () => {
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(true);
});

it('shows security column if security is enabled', () => {
expect(wrapper.find('.vulns').exists()).toBe(true);
});

it('computes pretty image title id', () => {
const prettyFormat = `sha256:${tag[0].image_id}`;

expect(wrapper.find('.image-id span').attributes().title).toBe(prettyFormat);
});

it('shows a short version of image id', () => {
const shortFormat = tag[0].image_id.substring(0, 12);

expect(wrapper.find('.image-id').text()).toBe(shortFormat);
});

it('shows pending if security scan didnt happen', () => {
const otherTag = Object.assign({}, tag[0], { scanned: 0 });

wrapper.setProps({ tag: [otherTag] });
expect(wrapper.find('.vulns').text()).toBe('Pending');
});

it('shows in progress if security scan started but didnt finish', () => {
const otherTag = Object.assign({}, tag[0], { scanned: 1 });

wrapper.setProps({ tag: [otherTag] });
expect(wrapper.find('.vulns').text()).toBe('In progress');
});

it('shows # of vulnerabilities if security scan finished', () => {
expect(wrapper.find('.vulns').text()).toBe('0 vulnerabilities');
});
});
69 changes: 69 additions & 0 deletions spec/javascripts/modules/repositories/tags-table.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { mount } from 'vue-test-utils';

import sinon from 'sinon';

import range from '~/utils/range';

import TagsTable from '~/modules/repositories/components/tags-table';

describe('tags-table', () => {
let wrapper;
const tags = range(1, 10).map(i => [
{
id: i,
name: 'latest',
author: {
id: i,
name: 'vitoravelino',
},
digest: 'sha256:4cee1979ba0bf7db9fc5d28fb7b798ca69ae95a47c5fecf46327720df4ff352d',
image_id: '5b0d59026729b68570d99bc4f3f7c31a2e4f2a5736435641565d93e7c25bd2c3',
created_at: '2018-02-06T12:54:31.000Z',
updated_at: '2018-02-06T12:54:31.000Z',
scanned: 2,
vulnerabilities: {
clair: [],
},
},
]);

beforeEach(() => {
wrapper = mount(TagsTable, {
propsData: {
limit: 3,
currentPage: 2,
state: {
selectedTags: [],
repository: {},
},
tags,
canDestroy: true,
securityEnabled: true,
},
mocks: {
$bus: {
$emit: sinon.spy(),
},
},
});
});

it('shows checkbox column if able to delete row', () => {
expect(wrapper.find('table input[type="checkbox"]').exists()).toBe(true);
});

it('shows security column if security is enabled', () => {
expect(wrapper.find('.vulns').exists()).toBe(true);
});

it('filters tags based on current page and itens per page', () => {
let currentTags = wrapper.vm.filteredTags.map(t => t[0].id);

expect(currentTags).toEqual([1, 2, 3]);

wrapper.setData({ currentPage: 2 });
currentTags = wrapper.vm.filteredTags.map(t => t[0].id);

expect(currentTags).toEqual([4, 5, 6]);
});
});
20 changes: 20 additions & 0 deletions spec/javascripts/shared/loading-icon.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { mount } from 'vue-test-utils';

import LoadingIcon from '~/shared/components/loading-icon';

describe('loading-icon', () => {
let wrapper;

beforeEach(() => {
wrapper = mount(LoadingIcon);
});

it('shows loading icon with normal size (2x)', () => {
expect(wrapper.find('.fa-spinner').classes()).toContain('fa-2x');
});

it('shows loading icon with custom size', () => {
wrapper.setProps({ size: 3 });
expect(wrapper.find('.fa-spinner').classes()).toContain('fa-3x');
});
});
Loading

0 comments on commit ecca2d9

Please sign in to comment.