Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
c2496044
Commit
c2496044
authored
Sep 23, 2019
by
Nicolò Maria Mezzopera
Committed by
Fatih Acet
Sep 23, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Unit test for collapsible container
Reachable code fully covered
parent
7c13f24e
Changes
13
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
627 additions
and
419 deletions
+627
-419
app/assets/javascripts/registry/components/app.vue
app/assets/javascripts/registry/components/app.vue
+4
-4
app/assets/javascripts/registry/components/collapsible_container.vue
...javascripts/registry/components/collapsible_container.vue
+3
-2
app/assets/javascripts/registry/components/table_registry.vue
...assets/javascripts/registry/components/table_registry.vue
+10
-8
spec/frontend/registry/components/app_spec.js
spec/frontend/registry/components/app_spec.js
+121
-0
spec/frontend/registry/components/collapsible_container_spec.js
...rontend/registry/components/collapsible_container_spec.js
+89
-0
spec/frontend/registry/components/table_registry_spec.js
spec/frontend/registry/components/table_registry_spec.js
+211
-0
spec/frontend/registry/mock_data.js
spec/frontend/registry/mock_data.js
+0
-0
spec/frontend/registry/stores/actions_spec.js
spec/frontend/registry/stores/actions_spec.js
+189
-0
spec/frontend/registry/stores/getters_spec.js
spec/frontend/registry/stores/getters_spec.js
+0
-0
spec/frontend/registry/stores/mutations_spec.js
spec/frontend/registry/stores/mutations_spec.js
+0
-0
spec/javascripts/registry/components/app_spec.js
spec/javascripts/registry/components/app_spec.js
+0
-129
spec/javascripts/registry/components/collapsible_container_spec.js
...scripts/registry/components/collapsible_container_spec.js
+0
-87
spec/javascripts/registry/components/table_registry_spec.js
spec/javascripts/registry/components/table_registry_spec.js
+0
-189
No files found.
app/assets/javascripts/registry/components/app.vue
View file @
c2496044
...
...
@@ -109,7 +109,7 @@ export default {
:svg-path=
"containersErrorImage"
>
<template
#description
>
<p
v-html=
"dockerConnectionErrorText"
></p>
<p
class=
"js-character-error-text"
v-html=
"dockerConnectionErrorText"
></p>
</
template
>
</gl-empty-state>
...
...
app/assets/javascripts/registry/components/collapsible_container.vue
View file @
c2496044
...
...
@@ -49,7 +49,7 @@ export default {
}
},
handleDeleteRepository
()
{
this
.
deleteItem
(
this
.
repo
)
return
this
.
deleteItem
(
this
.
repo
)
.
then
(()
=>
{
createFlash
(
__
(
'
This container registry has been scheduled for deletion.
'
),
'
notice
'
);
this
.
fetchRepos
();
...
...
@@ -67,7 +67,8 @@ export default {
<div
class=
"container-image"
>
<div
class=
"container-image-head"
>
<gl-button
class=
"js-toggle-repo btn-link align-baseline"
@
click=
"toggleRepo"
>
<icon
:name=
"iconName"
/>
{{
repo
.
name
}}
<icon
:name=
"iconName"
/>
{{
repo
.
name
}}
</gl-button>
<clipboard-button
...
...
app/assets/javascripts/registry/components/table_registry.vue
View file @
c2496044
...
...
@@ -198,8 +198,9 @@ export default {
:title=
"s__('ContainerRegistry|Remove selected images')"
:aria-label=
"s__('ContainerRegistry|Remove selected images')"
@
click=
"deleteMultipleItems()"
><icon
name=
"remove"
/></gl-button>
>
<icon
name=
"remove"
/>
</gl-button>
</th>
</tr>
</thead>
...
...
@@ -223,9 +224,9 @@ export default {
/>
</td>
<td>
<span
v-gl-tooltip
.
bottom
class=
"monospace"
:title=
"item.revision"
>
{{
item
.
shortRevision
}}
</span>
<span
v-gl-tooltip
.
bottom
class=
"monospace"
:title=
"item.revision"
>
{{
item
.
shortRevision
}}
</span>
</td>
<td>
{{
formatSize
(
item
.
size
)
}}
...
...
@@ -236,9 +237,9 @@ export default {
</td>
<td>
<span
v-gl-tooltip
.
bottom
:title=
"tooltipTitle(item.createdAt)"
>
{{ timeFormated(item.createdAt) }}
</span>
<span
v-gl-tooltip
.
bottom
:title=
"tooltipTitle(item.createdAt)"
>
{{
timeFormated(item.createdAt)
}}
</span>
</td>
<td
class=
"content action-buttons"
>
...
...
@@ -262,6 +263,7 @@ export default {
v-if=
"shouldRenderPagination"
:change=
"onPageChange"
:page-info=
"repo.pagination"
class=
"js-registry-pagination"
/>
<gl-modal
ref=
"deleteModal"
:modal-id=
"modalId"
ok-variant=
"danger"
>
...
...
spec/frontend/registry/components/app_spec.js
0 → 100644
View file @
c2496044
import
registry
from
'
~/registry/components/app.vue
'
;
import
{
mount
}
from
'
@vue/test-utils
'
;
import
{
TEST_HOST
}
from
'
../../helpers/test_constants
'
;
import
{
reposServerResponse
,
parsedReposServerResponse
}
from
'
../mock_data
'
;
describe
(
'
Registry List
'
,
()
=>
{
let
wrapper
;
const
findCollapsibleContainer
=
w
=>
w
.
findAll
({
name
:
'
CollapsibeContainerRegisty
'
});
const
findNoContainerImagesText
=
w
=>
w
.
find
(
'
.js-no-container-images-text
'
);
const
findSpinner
=
w
=>
w
.
find
(
'
.gl-spinner
'
);
const
findCharacterErrorText
=
w
=>
w
.
find
(
'
.js-character-error-text
'
);
const
propsData
=
{
endpoint
:
`
${
TEST_HOST
}
/foo`
,
helpPagePath
:
'
foo
'
,
noContainersImage
:
'
foo
'
,
containersErrorImage
:
'
foo
'
,
repositoryUrl
:
'
foo
'
,
};
const
setMainEndpoint
=
jest
.
fn
();
const
fetchRepos
=
jest
.
fn
();
const
methods
=
{
setMainEndpoint
,
fetchRepos
,
};
beforeEach
(()
=>
{
wrapper
=
mount
(
registry
,
{
propsData
,
computed
:
{
repos
()
{
return
parsedReposServerResponse
;
},
},
methods
,
});
});
describe
(
'
with data
'
,
()
=>
{
it
(
'
should render a list of CollapsibeContainerRegisty
'
,
()
=>
{
const
containers
=
findCollapsibleContainer
(
wrapper
);
expect
(
wrapper
.
vm
.
repos
.
length
).
toEqual
(
reposServerResponse
.
length
);
expect
(
containers
.
length
).
toEqual
(
reposServerResponse
.
length
);
});
});
describe
(
'
without data
'
,
()
=>
{
let
localWrapper
;
beforeEach
(()
=>
{
localWrapper
=
mount
(
registry
,
{
propsData
,
computed
:
{
repos
()
{
return
[];
},
},
methods
,
});
});
it
(
'
should render empty message
'
,
()
=>
{
const
noContainerImagesText
=
findNoContainerImagesText
(
localWrapper
);
expect
(
noContainerImagesText
.
text
()).
toEqual
(
'
With the Container Registry, every project can have its own space to store its Docker images. More Information
'
,
);
});
});
describe
(
'
while loading data
'
,
()
=>
{
let
localWrapper
;
beforeEach
(()
=>
{
localWrapper
=
mount
(
registry
,
{
propsData
,
computed
:
{
repos
()
{
return
[];
},
isLoading
()
{
return
true
;
},
},
methods
,
});
});
it
(
'
should render a loading spinner
'
,
()
=>
{
const
spinner
=
findSpinner
(
localWrapper
);
expect
(
spinner
.
exists
()).
toBe
(
true
);
});
});
describe
(
'
invalid characters in path
'
,
()
=>
{
let
localWrapper
;
beforeEach
(()
=>
{
localWrapper
=
mount
(
registry
,
{
propsData
:
{
...
propsData
,
characterError
:
true
,
},
computed
:
{
repos
()
{
return
[];
},
},
methods
,
});
});
it
(
'
should render invalid characters error message
'
,
()
=>
{
const
characterErrorText
=
findCharacterErrorText
(
localWrapper
);
expect
(
characterErrorText
.
text
()).
toEqual
(
'
We are having trouble connecting to Docker, which could be due to an issue with your project name or path. More Information
'
,
);
});
});
});
spec/frontend/registry/components/collapsible_container_spec.js
0 → 100644
View file @
c2496044
import
Vue
from
'
vue
'
;
import
{
mount
}
from
'
@vue/test-utils
'
;
import
collapsibleComponent
from
'
~/registry/components/collapsible_container.vue
'
;
import
{
repoPropsData
}
from
'
../mock_data
'
;
import
createFlash
from
'
~/flash
'
;
jest
.
mock
(
'
~/flash.js
'
);
describe
(
'
collapsible registry container
'
,
()
=>
{
let
wrapper
;
const
findDeleteBtn
=
w
=>
w
.
find
(
'
.js-remove-repo
'
);
const
findContainerImageTags
=
w
=>
w
.
find
(
'
.container-image-tags
'
);
const
findToggleRepos
=
w
=>
w
.
findAll
(
'
.js-toggle-repo
'
);
beforeEach
(()
=>
{
createFlash
.
mockClear
();
// This is needed due to console.error called by vue to emit a warning that stop the tests
// see https://github.com/vuejs/vue-test-utils/issues/532
Vue
.
config
.
silent
=
true
;
wrapper
=
mount
(
collapsibleComponent
,
{
propsData
:
{
repo
:
repoPropsData
,
},
});
});
afterEach
(()
=>
{
Vue
.
config
.
silent
=
false
;
});
describe
(
'
toggle
'
,
()
=>
{
beforeEach
(()
=>
{
const
fetchList
=
jest
.
fn
();
wrapper
.
setMethods
({
fetchList
});
});
const
expectIsClosed
=
()
=>
{
const
container
=
findContainerImageTags
(
wrapper
);
expect
(
container
.
exists
()).
toBe
(
false
);
expect
(
wrapper
.
vm
.
iconName
).
toEqual
(
'
angle-right
'
);
};
it
(
'
should be closed by default
'
,
()
=>
{
expectIsClosed
();
});
it
(
'
should be open when user clicks on closed repo
'
,
()
=>
{
const
toggleRepos
=
findToggleRepos
(
wrapper
);
toggleRepos
.
at
(
0
).
trigger
(
'
click
'
);
const
container
=
findContainerImageTags
(
wrapper
);
expect
(
container
.
exists
()).
toBe
(
true
);
expect
(
wrapper
.
vm
.
fetchList
).
toHaveBeenCalled
();
});
it
(
'
should be closed when the user clicks on an opened repo
'
,
done
=>
{
const
toggleRepos
=
findToggleRepos
(
wrapper
);
toggleRepos
.
at
(
0
).
trigger
(
'
click
'
);
Vue
.
nextTick
(()
=>
{
toggleRepos
.
at
(
0
).
trigger
(
'
click
'
);
Vue
.
nextTick
(()
=>
{
expectIsClosed
();
done
();
});
});
});
});
describe
(
'
delete repo
'
,
()
=>
{
it
(
'
should be possible to delete a repo
'
,
()
=>
{
const
deleteBtn
=
findDeleteBtn
(
wrapper
);
expect
(
deleteBtn
.
exists
()).
toBe
(
true
);
});
it
(
'
should call deleteItem when confirming deletion
'
,
()
=>
{
const
deleteItem
=
jest
.
fn
().
mockResolvedValue
();
const
fetchRepos
=
jest
.
fn
().
mockResolvedValue
();
wrapper
.
setMethods
({
deleteItem
,
fetchRepos
});
wrapper
.
vm
.
handleDeleteRepository
();
expect
(
wrapper
.
vm
.
deleteItem
).
toHaveBeenCalledWith
(
wrapper
.
vm
.
repo
);
});
it
(
'
should show an error when there is API error
'
,
()
=>
{
const
deleteItem
=
jest
.
fn
().
mockRejectedValue
(
'
error
'
);
wrapper
.
setMethods
({
deleteItem
});
return
wrapper
.
vm
.
handleDeleteRepository
().
then
(()
=>
{
expect
(
createFlash
).
toHaveBeenCalled
();
});
});
});
});
spec/frontend/registry/components/table_registry_spec.js
0 → 100644
View file @
c2496044
import
Vue
from
'
vue
'
;
import
tableRegistry
from
'
~/registry/components/table_registry.vue
'
;
import
{
mount
}
from
'
@vue/test-utils
'
;
import
{
repoPropsData
}
from
'
../mock_data
'
;
const
[
firstImage
,
secondImage
]
=
repoPropsData
.
list
;
describe
(
'
table registry
'
,
()
=>
{
let
wrapper
;
const
findSelectAllCheckbox
=
w
=>
w
.
find
(
'
.js-select-all-checkbox > input
'
);
const
findSelectCheckboxes
=
w
=>
w
.
findAll
(
'
.js-select-checkbox > input
'
);
const
findDeleteButton
=
w
=>
w
.
find
(
'
.js-delete-registry
'
);
const
findDeleteButtonsRow
=
w
=>
w
.
findAll
(
'
.js-delete-registry-row
'
);
const
findPagination
=
w
=>
w
.
find
(
'
.js-registry-pagination
'
);
const
bulkDeletePath
=
'
path
'
;
beforeEach
(()
=>
{
// This is needed due to console.error called by vue to emit a warning that stop the tests
// see https://github.com/vuejs/vue-test-utils/issues/532
Vue
.
config
.
silent
=
true
;
wrapper
=
mount
(
tableRegistry
,
{
propsData
:
{
repo
:
repoPropsData
,
},
});
});
afterEach
(()
=>
{
Vue
.
config
.
silent
=
false
;
});
describe
(
'
rendering
'
,
()
=>
{
it
(
'
should render a table with the registry list
'
,
()
=>
{
expect
(
wrapper
.
findAll
(
'
.registry-image-row
'
).
length
).
toEqual
(
repoPropsData
.
list
.
length
);
});
it
(
'
should render registry tag
'
,
()
=>
{
const
tds
=
wrapper
.
findAll
(
'
.registry-image-row td
'
);
expect
(
tds
.
at
(
0
).
classes
()).
toContain
(
'
check
'
);
expect
(
tds
.
at
(
1
).
html
()).
toContain
(
repoPropsData
.
list
[
0
].
tag
);
expect
(
tds
.
at
(
2
).
html
()).
toContain
(
repoPropsData
.
list
[
0
].
shortRevision
);
expect
(
tds
.
at
(
3
).
html
()).
toContain
(
repoPropsData
.
list
[
0
].
layers
);
expect
(
tds
.
at
(
3
).
html
()).
toContain
(
repoPropsData
.
list
[
0
].
size
);
expect
(
tds
.
at
(
4
).
html
()).
toContain
(
wrapper
.
vm
.
timeFormated
(
repoPropsData
.
list
[
0
].
createdAt
));
});
});
describe
(
'
multi select
'
,
()
=>
{
it
(
'
selecting a row should enable delete button
'
,
done
=>
{
const
deleteBtn
=
findDeleteButton
(
wrapper
);
const
checkboxes
=
findSelectCheckboxes
(
wrapper
);
expect
(
deleteBtn
.
attributes
(
'
disabled
'
)).
toBe
(
'
disabled
'
);
checkboxes
.
at
(
0
).
trigger
(
'
click
'
);
Vue
.
nextTick
(()
=>
{
expect
(
deleteBtn
.
attributes
(
'
disabled
'
)).
toEqual
(
undefined
);
done
();
});
});
it
(
'
selecting all checkbox should select all rows and enable delete button
'
,
done
=>
{
const
selectAll
=
findSelectAllCheckbox
(
wrapper
);
const
checkboxes
=
findSelectCheckboxes
(
wrapper
);
selectAll
.
trigger
(
'
click
'
);
Vue
.
nextTick
(()
=>
{
const
checked
=
checkboxes
.
filter
(
w
=>
w
.
element
.
checked
);
expect
(
checked
.
length
).
toBe
(
checkboxes
.
length
);
done
();
});
});
it
(
'
deselecting select all checkbox should deselect all rows and disable delete button
'
,
done
=>
{
const
checkboxes
=
findSelectCheckboxes
(
wrapper
);
const
selectAll
=
findSelectAllCheckbox
(
wrapper
);
selectAll
.
trigger
(
'
click
'
);
selectAll
.
trigger
(
'
click
'
);
Vue
.
nextTick
(()
=>
{
const
checked
=
checkboxes
.
filter
(
w
=>
!
w
.
element
.
checked
);
expect
(
checked
.
length
).
toBe
(
checkboxes
.
length
);
done
();
});
});
it
(
'
should delete multiple items when multiple items are selected
'
,
done
=>
{
const
multiDeleteItems
=
jest
.
fn
().
mockResolvedValue
();
wrapper
.
setMethods
({
multiDeleteItems
});
const
selectAll
=
findSelectAllCheckbox
(
wrapper
);
selectAll
.
trigger
(
'
click
'
);
Vue
.
nextTick
(()
=>
{
const
deleteBtn
=
findDeleteButton
(
wrapper
);
expect
(
wrapper
.
vm
.
itemsToBeDeleted
).
toEqual
([
0
,
1
]);
expect
(
deleteBtn
.
attributes
(
'
disabled
'
)).
toEqual
(
undefined
);
wrapper
.
vm
.
handleMultipleDelete
();
Vue
.
nextTick
(()
=>
{
expect
(
wrapper
.
vm
.
itemsToBeDeleted
).
toEqual
([]);
expect
(
wrapper
.
vm
.
multiDeleteItems
).
toHaveBeenCalledWith
({
path
:
bulkDeletePath
,
items
:
[
firstImage
.
tag
,
secondImage
.
tag
],
});
done
();
});
});
});
it
(
'
should show an error message if bulkDeletePath is not set
'
,
()
=>
{
const
showError
=
jest
.
fn
();
wrapper
.
setMethods
({
showError
});
wrapper
.
setProps
({
repo
:
{
...
repoPropsData
,
tagsPath
:
null
,
},
});
wrapper
.
vm
.
handleMultipleDelete
();
expect
(
wrapper
.
vm
.
showError
).
toHaveBeenCalled
();
});
});
describe
(
'
delete registry
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
.
setData
({
itemsToBeDeleted
:
[
0
]
});
});
it
(
'
should be possible to delete a registry
'
,
()
=>
{
const
deleteBtn
=
findDeleteButton
(
wrapper
);
const
deleteBtns
=
findDeleteButtonsRow
(
wrapper
);
expect
(
wrapper
.
vm
.
itemsToBeDeleted
).
toEqual
([
0
]);
expect
(
deleteBtn
).
toBeDefined
();
expect
(
deleteBtn
.
attributes
(
'
disable
'
)).
toBe
(
undefined
);
expect
(
deleteBtns
.
is
(
'
button
'
)).
toBe
(
true
);
});
it
(
'
should allow deletion row by row
'
,
()
=>
{
const
deleteBtns
=
findDeleteButtonsRow
(
wrapper
);
const
deleteSingleItem
=
jest
.
fn
();
const
deleteItem
=
jest
.
fn
().
mockResolvedValue
();
wrapper
.
setMethods
({
deleteSingleItem
,
deleteItem
});
deleteBtns
.
at
(
0
).
trigger
(
'
click
'
);
expect
(
wrapper
.
vm
.
deleteSingleItem
).
toHaveBeenCalledWith
(
0
);
wrapper
.
vm
.
handleSingleDelete
(
1
);
expect
(
wrapper
.
vm
.
deleteItem
).
toHaveBeenCalledWith
(
1
);
});
});
describe
(
'
pagination
'
,
()
=>
{
let
localWrapper
=
null
;
const
repo
=
{
repoPropsData
,
pagination
:
{
total
:
20
,
perPage
:
2
,
nextPage
:
2
,
},
};
beforeEach
(()
=>
{
localWrapper
=
mount
(
tableRegistry
,
{
propsData
:
{
repo
,
},
});
});
it
(
'
should exist
'
,
()
=>
{
const
pagination
=
findPagination
(
localWrapper
);
expect
(
pagination
.
exists
()).
toBe
(
true
);
});
it
(
'
should be visible when pagination is needed
'
,
()
=>
{
const
pagination
=
findPagination
(
localWrapper
);
expect
(
pagination
.
isVisible
()).
toBe
(
true
);
localWrapper
.
setProps
({
repo
:
{
pagination
:
{
total
:
0
,
perPage
:
10
,
},
},
});
expect
(
localWrapper
.
vm
.
shouldRenderPagination
).
toBe
(
false
);
});
it
(
'
should have a change function that update the list when run
'
,
()
=>
{
const
fetchList
=
jest
.
fn
().
mockResolvedValue
();
localWrapper
.
setMethods
({
fetchList
});
localWrapper
.
vm
.
onPageChange
(
1
);
expect
(
localWrapper
.
vm
.
fetchList
).
toHaveBeenCalledWith
({
repo
,
page
:
1
});
});
});
describe
(
'
modal content
'
,
()
=>
{
it
(
'
should show the singular title and image name when deleting a single image
'
,
()
=>
{
wrapper
.
setData
({
itemsToBeDeleted
:
[
1
]
});
wrapper
.
vm
.
setModalDescription
(
0
);
expect
(
wrapper
.
vm
.
modalTitle
).
toBe
(
'
Remove image
'
);
expect
(
wrapper
.
vm
.
modalDescription
).
toContain
(
firstImage
.
tag
);
});
it
(
'
should show the plural title and image count when deleting more than one image
'
,
()
=>
{
wrapper
.
setData
({
itemsToBeDeleted
:
[
1
,
2
]
});
wrapper
.
vm
.
setModalDescription
();
expect
(
wrapper
.
vm
.
modalTitle
).
toBe
(
'
Remove images
'
);
expect
(
wrapper
.
vm
.
modalDescription
).
toContain
(
'
<b>2</b> images
'
);
});
});
});
spec/
javascripts
/registry/mock_data.js
→
spec/
frontend
/registry/mock_data.js
View file @
c2496044
File moved
spec/
javascripts
/registry/stores/actions_spec.js
→
spec/
frontend
/registry/stores/actions_spec.js
View file @
c2496044
...
...
@@ -2,30 +2,33 @@ import MockAdapter from 'axios-mock-adapter';
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
*
as
actions
from
'
~/registry/stores/actions
'
;
import
*
as
types
from
'
~/registry/stores/mutation_types
'
;
import
state
from
'
~/registry/stores/state
'
;
import
{
TEST_HOST
}
from
'
spec/test_constants
'
;
import
{
TEST_HOST
}
from
'
../../helpers/test_constants
'
;
import
testAction
from
'
../../helpers/vuex_action_helper
'
;
import
createFlash
from
'
~/flash
'
;
import
{
reposServerResponse
,
registryServerResponse
,
parsedReposServerResponse
,
}
from
'
../mock_data
'
;
jest
.
mock
(
'
~/flash.js
'
);
describe
(
'
Actions Registry Store
'
,
()
=>
{
let
mockedState
;
let
mock
;
let
state
;
beforeEach
(()
=>
{
mockedState
=
state
();
mockedState
.
endpoint
=
`
${
TEST_HOST
}
/endpoint.json`
;
mock
=
new
MockAdapter
(
axios
);
state
=
{
endpoint
:
`
${
TEST_HOST
}
/endpoint.json`
,
};
});
afterEach
(()
=>
{
mock
.
restore
();
});
describe
(
'
server requests
'
,
()
=>
{
describe
(
'
fetchRepos
'
,
()
=>
{
beforeEach
(()
=>
{
mock
.
onGet
(
`
${
TEST_HOST
}
/endpoint.json`
).
replyOnce
(
200
,
reposServerResponse
,
{});
...
...
@@ -35,7 +38,7 @@ describe('Actions Registry Store', () => {
testAction
(
actions
.
fetchRepos
,
null
,
mockedS
tate
,
s
tate
,
[
{
type
:
types
.
TOGGLE_MAIN_LOADING
},
{
type
:
types
.
TOGGLE_MAIN_LOADING
},
...
...
@@ -45,14 +48,29 @@ describe('Actions Registry Store', () => {
done
,
);
});
it
(
'
should create flash on API error
'
,
done
=>
{
testAction
(
actions
.
fetchRepos
,
null
,
{
endpoint
:
null
,
},
[{
type
:
types
.
TOGGLE_MAIN_LOADING
},
{
type
:
types
.
TOGGLE_MAIN_LOADING
}],
[],
()
=>
{
expect
(
createFlash
).
toHaveBeenCalled
();
done
();
},
);
});
});
describe
(
'
fetchList
'
,
()
=>
{
let
repo
;
beforeEach
(()
=>
{
mockedState
.
repos
=
parsedReposServerResponse
;
[,
repo
]
=
mockedState
.
repos
;
state
.
repos
=
parsedReposServerResponse
;
[,
repo
]
=
state
.
repos
;
mock
.
onGet
(
repo
.
tagsPath
).
replyOnce
(
200
,
registryServerResponse
,
{});
});
...
...
@@ -60,7 +78,7 @@ describe('Actions Registry Store', () => {
testAction
(
actions
.
fetchList
,
{
repo
},
mockedS
tate
,
s
tate
,
[
{
type
:
types
.
TOGGLE_REGISTRY_LIST_LOADING
,
payload
:
repo
},
{
type
:
types
.
TOGGLE_REGISTRY_LIST_LOADING
,
payload
:
repo
},
...
...
@@ -69,7 +87,7 @@ describe('Actions Registry Store', () => {
payload
:
{
repo
,
resp
:
registryServerResponse
,
headers
:
jasmine
.
anything
(),
headers
:
expect
.
anything
(),
},
},
],
...
...
@@ -77,6 +95,28 @@ describe('Actions Registry Store', () => {
done
,
);
});
it
(
'
should create flash on API error
'
,
done
=>
{
const
updatedRepo
=
{
...
repo
,
tagsPath
:
null
,
};
testAction
(
actions
.
fetchList
,
{
repo
:
updatedRepo
,
},
state
,
[
{
type
:
types
.
TOGGLE_REGISTRY_LIST_LOADING
,
payload
:
updatedRepo
},
{
type
:
types
.
TOGGLE_REGISTRY_LIST_LOADING
,
payload
:
updatedRepo
},
],
[],
()
=>
{
expect
(
createFlash
).
toHaveBeenCalled
();
done
();
},
);
});
});
...
...
@@ -85,7 +125,7 @@ describe('Actions Registry Store', () => {
testAction
(
actions
.
setMainEndpoint
,
'
endpoint
'
,
mockedS
tate
,
s
tate
,
[{
type
:
types
.
SET_MAIN_ENDPOINT
,
payload
:
'
endpoint
'
}],
[],
done
,
...
...
@@ -98,7 +138,7 @@ describe('Actions Registry Store', () => {
testAction
(
actions
.
toggleLoading
,
null
,
mockedS
tate
,
s
tate
,
[{
type
:
types
.
TOGGLE_MAIN_LOADING
}],
[],
done
,
...
...
@@ -106,25 +146,42 @@ describe('Actions Registry Store', () => {
});
});
describe
(
'
deleteItem
'
,
()
=>
{
it
(
'
should perform DELETE request on destroyPath
'
,
done
=>
{
describe
(
'
deleteItem
and multiDeleteItems
'
,
()
=>
{
let
deleted
;
const
destroyPath
=
`
${
TEST_HOST
}
/mygroup/myproject/container_registry/1.json`
;
let
deleted
=
false
;
const
expectDelete
=
done
=>
{
expect
(
mock
.
history
.
delete
.
length
).
toBe
(
1
);
expect
(
deleted
).
toBe
(
true
);
done
();
};
beforeEach
(()
=>
{
deleted
=
false
;
mock
.
onDelete
(
destroyPath
).
replyOnce
(()
=>
{
deleted
=
true
;
return
[
200
];
});
});
it
(
'
deleteItem should perform DELETE request on destroyPath
'
,
done
=>
{
testAction
(
actions
.
deleteItem
,
{
destroyPath
,
},
mockedS
tate
,
s
tate
,
)
.
then
(()
=>
{
expect
(
mock
.
history
.
delete
.
length
).
toBe
(
1
);
expect
(
deleted
).
toBe
(
true
);
done
();
expectDelete
(
done
);
})
.
catch
(
done
.
fail
);
});
it
(
'
multiDeleteItems should perform DELETE request on path
'
,
done
=>
{
testAction
(
actions
.
multiDeleteItems
,
{
path
:
destroyPath
,
items
:
[
1
]
},
state
)
.
then
(()
=>
{
expectDelete
(
done
);
})
.
catch
(
done
.
fail
);
});
...
...
spec/frontend/registry/getters_spec.js
→
spec/frontend/registry/
stores/
getters_spec.js
View file @
c2496044
File moved
spec/
javascripts
/registry/stores/mutations_spec.js
→
spec/
frontend
/registry/stores/mutations_spec.js
View file @
c2496044
File moved
spec/javascripts/registry/components/app_spec.js
deleted
100644 → 0
View file @
7c13f24e
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
Vue
from
'
vue
'
;
import
registry
from
'
~/registry/components/app.vue
'
;
import
mountComponent
from
'
spec/helpers/vue_mount_component_helper
'
;
import
{
TEST_HOST
}
from
'
spec/test_constants
'
;
import
{
reposServerResponse
}
from
'
../mock_data
'
;
describe
(
'
Registry List
'
,
()
=>
{
const
Component
=
Vue
.
extend
(
registry
);
const
props
=
{
endpoint
:
`
${
TEST_HOST
}
/foo`
,
helpPagePath
:
'
foo
'
,
noContainersImage
:
'
foo
'
,
containersErrorImage
:
'
foo
'
,
repositoryUrl
:
'
foo
'
,
};
let
vm
;
let
mock
;
beforeEach
(()
=>
{
mock
=
new
MockAdapter
(
axios
);
});
afterEach
(()
=>
{
mock
.
restore
();
vm
.
$destroy
();
});
describe
(
'
with data
'
,
()
=>
{
beforeEach
(()
=>
{
mock
.
onGet
(
`
${
TEST_HOST
}
/foo`
).
replyOnce
(
200
,
reposServerResponse
);
vm
=
mountComponent
(
Component
,
{
...
props
});
});
it
(
'
should render a list of repos
'
,
done
=>
{
setTimeout
(()
=>
{
expect
(
vm
.
$store
.
state
.
repos
.
length
).
toEqual
(
reposServerResponse
.
length
);
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelectorAll
(
'
.container-image
'
).
length
).
toEqual
(
reposServerResponse
.
length
,
);
done
();
});
},
0
);
});
describe
(
'
delete repository
'
,
()
=>
{
it
(
'
should be possible to delete a repo
'
,
done
=>
{
setTimeout
(()
=>
{
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.container-image-head .js-remove-repo
'
)).
toBeDefined
();
done
();
});
},
0
);
});
});
describe
(
'
toggle repository
'
,
()
=>
{
it
(
'
should open the container
'
,
done
=>
{
setTimeout
(()
=>
{
Vue
.
nextTick
(()
=>
{
vm
.
$el
.
querySelector
(
'
.js-toggle-repo
'
).
click
();
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-toggle-repo use
'
).
getAttribute
(
'
xlink:href
'
),
).
toContain
(
'
angle-up
'
);
done
();
});
});
},
0
);
});
});
});
describe
(
'
without data
'
,
()
=>
{
beforeEach
(()
=>
{
mock
.
onGet
(
`
${
TEST_HOST
}
/foo`
).
replyOnce
(
200
,
[]);
vm
=
mountComponent
(
Component
,
{
...
props
});
});
it
(
'
should render empty message
'
,
done
=>
{
setTimeout
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-no-container-images-text
'
).
textContent
).
toEqual
(
'
With the Container Registry, every project can have its own space to store its Docker images. More Information
'
,
);
done
();
},
0
);
});
});
describe
(
'
while loading data
'
,
()
=>
{
beforeEach
(()
=>
{
mock
.
onGet
(
`
${
TEST_HOST
}
/foo`
).
replyOnce
(
200
,
[]);
vm
=
mountComponent
(
Component
,
{
...
props
});
});
it
(
'
should render a loading spinner
'
,
done
=>
{
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.gl-spinner
'
)).
not
.
toBe
(
null
);
done
();
});
});
});
describe
(
'
invalid characters in path
'
,
()
=>
{
beforeEach
(()
=>
{
mock
.
onGet
(
`
${
TEST_HOST
}
/foo`
).
replyOnce
(
200
,
[]);
vm
=
mountComponent
(
Component
,
{
...
props
,
characterError
:
true
,
});
});
it
(
'
should render invalid characters error message
'
,
done
=>
{
setTimeout
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
p
'
)).
not
.
toContain
(
'
We are having trouble connecting to Docker, which could be due to an issue with your project name or path. More information
'
,
);
done
();
});
});
});
});
spec/javascripts/registry/components/collapsible_container_spec.js
deleted
100644 → 0
View file @
7c13f24e
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
Vue
from
'
vue
'
;
import
collapsibleComponent
from
'
~/registry/components/collapsible_container.vue
'
;
import
store
from
'
~/registry/stores
'
;
import
*
as
types
from
'
~/registry/stores/mutation_types
'
;
import
{
repoPropsData
,
registryServerResponse
,
reposServerResponse
}
from
'
../mock_data
'
;
describe
(
'
collapsible registry container
'
,
()
=>
{
let
vm
;
let
mock
;
const
Component
=
Vue
.
extend
(
collapsibleComponent
);
const
findDeleteBtn
=
()
=>
vm
.
$el
.
querySelector
(
'
.js-remove-repo
'
);
beforeEach
(()
=>
{
mock
=
new
MockAdapter
(
axios
);
mock
.
onGet
(
repoPropsData
.
tagsPath
).
replyOnce
(
200
,
registryServerResponse
,
{});
store
.
commit
(
types
.
SET_REPOS_LIST
,
reposServerResponse
);
vm
=
new
Component
({
store
,
propsData
:
{
repo
:
repoPropsData
,
},
}).
$mount
();
});
afterEach
(()
=>
{
mock
.
restore
();
vm
.
$destroy
();
});
describe
(
'
toggle
'
,
()
=>
{
it
(
'
should be closed by default
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.container-image-tags
'
)).
toBe
(
null
);
expect
(
vm
.
iconName
).
toEqual
(
'
angle-right
'
);
});
it
(
'
should be open when user clicks on closed repo
'
,
done
=>
{
vm
.
$el
.
querySelector
(
'
.js-toggle-repo
'
).
click
();
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.container-image-tags
'
)).
not
.
toBeNull
();
expect
(
vm
.
iconName
).
toEqual
(
'
angle-up
'
);
done
();
});
});
it
(
'
should be closed when the user clicks on an opened repo
'
,
done
=>
{
vm
.
$el
.
querySelector
(
'
.js-toggle-repo
'
).
click
();
Vue
.
nextTick
(()
=>
{
vm
.
$el
.
querySelector
(
'
.js-toggle-repo
'
).
click
();
setTimeout
(()
=>
{
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.container-image-tags
'
)).
toBe
(
null
);
expect
(
vm
.
iconName
).
toEqual
(
'
angle-right
'
);
done
();
});
});
});
});
});
describe
(
'
delete repo
'
,
()
=>
{
it
(
'
should be possible to delete a repo
'
,
()
=>
{
expect
(
findDeleteBtn
()).
not
.
toBeNull
();
});
it
(
'
should call deleteItem when confirming deletion
'
,
done
=>
{
findDeleteBtn
().
click
();
spyOn
(
vm
,
'
deleteItem
'
).
and
.
returnValue
(
Promise
.
resolve
());
Vue
.
nextTick
(()
=>
{
document
.
querySelector
(
`#
${
vm
.
modalId
}
.btn-danger`
).
click
();
expect
(
vm
.
deleteItem
).
toHaveBeenCalledWith
(
vm
.
repo
);
done
();
});
});
});
});
spec/javascripts/registry/components/table_registry_spec.js
deleted
100644 → 0
View file @
7c13f24e
import
Vue
from
'
vue
'
;
import
tableRegistry
from
'
~/registry/components/table_registry.vue
'
;
import
store
from
'
~/registry/stores
'
;
import
{
mountComponentWithStore
}
from
'
spec/helpers/vue_mount_component_helper
'
;
import
{
repoPropsData
}
from
'
../mock_data
'
;
const
[
firstImage
,
secondImage
]
=
repoPropsData
.
list
;
describe
(
'
table registry
'
,
()
=>
{
let
vm
;
const
Component
=
Vue
.
extend
(
tableRegistry
);
const
bulkDeletePath
=
'
path
'
;
const
findDeleteBtn
=
()
=>
vm
.
$el
.
querySelector
(
'
.js-delete-registry
'
);
const
findDeleteBtnRow
=
()
=>
vm
.
$el
.
querySelector
(
'
.js-delete-registry-row
'
);
const
findSelectAllCheckbox
=
()
=>
vm
.
$el
.
querySelector
(
'
.js-select-all-checkbox > input
'
);
const
findAllRowCheckboxes
=
()
=>
Array
.
from
(
vm
.
$el
.
querySelectorAll
(
'
.js-select-checkbox input
'
));
const
confirmationModal
=
(
child
=
''
)
=>
document
.
querySelector
(
`#
${
vm
.
modalId
}
${
child
}
`
);
const
createComponent
=
()
=>
{
vm
=
mountComponentWithStore
(
Component
,
{
store
,
props
:
{
repo
:
repoPropsData
,
},
});
};
const
selectAllCheckboxes
=
()
=>
vm
.
selectAll
();
const
deselectAllCheckboxes
=
()
=>
vm
.
deselectAll
();
beforeEach
(()
=>
{
createComponent
();
});
afterEach
(()
=>
{
vm
.
$destroy
();
});
describe
(
'
rendering
'
,
()
=>
{
it
(
'
should render a table with the registry list
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelectorAll
(
'
table tbody tr
'
).
length
).
toEqual
(
repoPropsData
.
list
.
length
);
});
it
(
'
should render registry tag
'
,
()
=>
{
const
textRendered
=
vm
.
$el
.
querySelector
(
'
.table tbody tr
'
)
.
textContent
.
trim
()
// replace additional whitespace characters (e.g. new lines) with a single empty space
.
replace
(
/
\s\s
+/g
,
'
'
);
expect
(
textRendered
).
toContain
(
repoPropsData
.
list
[
0
].
tag
);
expect
(
textRendered
).
toContain
(
repoPropsData
.
list
[
0
].
shortRevision
);
expect
(
textRendered
).
toContain
(
repoPropsData
.
list
[
0
].
layers
);
expect
(
textRendered
).
toContain
(
repoPropsData
.
list
[
0
].
size
);
});
});
describe
(
'
multi select
'
,
()
=>
{
it
(
'
should support multiselect and selecting a row should enable delete button
'
,
done
=>
{
findSelectAllCheckbox
().
click
();
selectAllCheckboxes
();
expect
(
findSelectAllCheckbox
().
checked
).
toBe
(
true
);
Vue
.
nextTick
(()
=>
{
expect
(
findDeleteBtn
().
disabled
).
toBe
(
false
);
done
();
});
});
it
(
'
selecting all checkbox should select all rows and enable delete button
'
,
done
=>
{
selectAllCheckboxes
();
Vue
.
nextTick
(()
=>
{
const
checkedValues
=
findAllRowCheckboxes
().
filter
(
x
=>
x
.
checked
);
expect
(
checkedValues
.
length
).
toBe
(
repoPropsData
.
list
.
length
);
done
();
});
});
it
(
'
deselecting select all checkbox should deselect all rows and disable delete button
'
,
done
=>
{
selectAllCheckboxes
();
deselectAllCheckboxes
();
Vue
.
nextTick
(()
=>
{
const
checkedValues
=
findAllRowCheckboxes
().
filter
(
x
=>
x
.
checked
);
expect
(
checkedValues
.
length
).
toBe
(
0
);
done
();
});
});
it
(
'
should delete multiple items when multiple items are selected
'
,
done
=>
{
selectAllCheckboxes
();
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
itemsToBeDeleted
).
toEqual
([
0
,
1
]);
expect
(
findDeleteBtn
().
disabled
).
toBe
(
false
);
findDeleteBtn
().
click
();
spyOn
(
vm
,
'
multiDeleteItems
'
).
and
.
returnValue
(
Promise
.
resolve
());
Vue
.
nextTick
(()
=>
{
const
modal
=
confirmationModal
();
confirmationModal
(
'
.btn-danger
'
).
click
();
expect
(
modal
).
toExist
();
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
itemsToBeDeleted
).
toEqual
([]);
expect
(
vm
.
multiDeleteItems
).
toHaveBeenCalledWith
({
path
:
bulkDeletePath
,
items
:
[
firstImage
.
tag
,
secondImage
.
tag
],
});
done
();
});
});
});
});
});
describe
(
'
delete registry
'
,
()
=>
{
beforeEach
(()
=>
{
vm
.
itemsToBeDeleted
=
[
0
];
});
it
(
'
should be possible to delete a registry
'
,
done
=>
{
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
itemsToBeDeleted
).
toEqual
([
0
]);
expect
(
findDeleteBtn
()).
toBeDefined
();
expect
(
findDeleteBtn
().
disabled
).
toBe
(
false
);
expect
(
findDeleteBtnRow
()).
toBeDefined
();
done
();
});
});
it
(
'
should call deleteItems and reset itemsToBeDeleted when confirming deletion
'
,
done
=>
{
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
itemsToBeDeleted
).
toEqual
([
0
]);
expect
(
findDeleteBtn
().
disabled
).
toBe
(
false
);
findDeleteBtn
().
click
();
spyOn
(
vm
,
'
multiDeleteItems
'
).
and
.
returnValue
(
Promise
.
resolve
());
Vue
.
nextTick
(()
=>
{
confirmationModal
(
'
.btn-danger
'
).
click
();
expect
(
vm
.
itemsToBeDeleted
).
toEqual
([]);
expect
(
vm
.
multiDeleteItems
).
toHaveBeenCalledWith
({
path
:
bulkDeletePath
,
items
:
[
firstImage
.
tag
],
});
done
();
});
});
});
});
describe
(
'
pagination
'
,
()
=>
{
it
(
'
should be possible to change the page
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.gl-pagination
'
)).
toBeDefined
();
});
});
describe
(
'
modal content
'
,
()
=>
{
it
(
'
should show the singular title and image name when deleting a single image
'
,
done
=>
{
findDeleteBtnRow
().
click
();
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
modalTitle
).
toBe
(
'
Remove image
'
);
expect
(
vm
.
modalDescription
).
toContain
(
firstImage
.
tag
);
done
();
});
});
it
(
'
should show the plural title and image count when deleting more than one image
'
,
done
=>
{
selectAllCheckboxes
();
vm
.
setModalDescription
();
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
modalTitle
).
toBe
(
'
Remove images
'
);
expect
(
vm
.
modalDescription
).
toContain
(
'
<b>2</b> images
'
);
done
();
});
});
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment