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

js: added unit tests for vue components and utils #1661

Merged
merged 1 commit into from
Feb 7, 2018
Merged
Show file tree
Hide file tree
Changes from all 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
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