Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-10-28 15:11:31 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-10-28 15:11:31 +0300
commit2ebd699ede8f213f6e8f21ba7d1d9904197b2984 (patch)
treeea8a020f8bc1ffce42e95f76629c72c59e94a7be /spec/frontend
parent25788905108838d95a62d7e3ad3ca16e6f6d0fda (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
-rw-r--r--spec/frontend/behaviors/markdown/render_sandboxed_mermaid_spec.js145
-rw-r--r--spec/frontend/issues/list/components/issues_list_app_spec.js56
-rw-r--r--spec/frontend/notebook/cells/markdown_spec.js101
-rw-r--r--spec/frontend/notebook/cells/output/index_spec.js14
-rw-r--r--spec/frontend/notebook/cells/output/markdown_spec.js44
-rw-r--r--spec/frontend/notebook/mock_data.js2
-rw-r--r--spec/frontend/releases/components/asset_links_form_spec.js42
7 files changed, 292 insertions, 112 deletions
diff --git a/spec/frontend/behaviors/markdown/render_sandboxed_mermaid_spec.js b/spec/frontend/behaviors/markdown/render_sandboxed_mermaid_spec.js
index 2b9442162aa..de0e5063e49 100644
--- a/spec/frontend/behaviors/markdown/render_sandboxed_mermaid_spec.js
+++ b/spec/frontend/behaviors/markdown/render_sandboxed_mermaid_spec.js
@@ -1,34 +1,127 @@
-import $ from 'jquery';
+import { createWrapper } from '@vue/test-utils';
+import { __ } from '~/locale';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
-import renderMermaid from '~/behaviors/markdown/render_sandboxed_mermaid';
+import renderMermaid, {
+ MAX_CHAR_LIMIT,
+ MAX_MERMAID_BLOCK_LIMIT,
+ LAZY_ALERT_SHOWN_CLASS,
+} from '~/behaviors/markdown/render_sandboxed_mermaid';
-describe('Render mermaid diagrams for Gitlab Flavoured Markdown', () => {
- it('Does something', () => {
- document.body.dataset.page = '';
- setHTMLFixture(`
- <div class="gl-relative markdown-code-block js-markdown-code">
- <pre data-sourcepos="1:1-7:3" class="code highlight js-syntax-highlight language-mermaid white" lang="mermaid" id="code-4">
- <code class="js-render-mermaid">
- <span id="LC1" class="line" lang="mermaid">graph TD;</span>
- <span id="LC2" class="line" lang="mermaid">A--&gt;B</span>
- <span id="LC3" class="line" lang="mermaid">A--&gt;C</span>
- <span id="LC4" class="line" lang="mermaid">B--&gt;D</span>
- <span id="LC5" class="line" lang="mermaid">C--&gt;D</span>
- </code>
- </pre>
- <copy-code>
- <button type="button" class="btn btn-default btn-md gl-button btn-icon has-tooltip" data-title="Copy to clipboard" data-clipboard-target="pre#code-4">
- <svg><use xlink:href="/assets/icons-7f1680a3670112fe4c8ef57b9dfb93f0f61b43a2a479d7abd6c83bcb724b9201.svg#copy-to-clipboard"></use></svg>
- </button>
- </copy-code>
- </div>`);
- const els = $('pre.js-syntax-highlight').find('.js-render-mermaid');
-
- renderMermaid(els);
+describe('Mermaid diagrams renderer', () => {
+ // Finders
+ const findMermaidIframes = () => document.querySelectorAll('iframe[src="/-/sandbox/mermaid"]');
+ const findDangerousMermaidAlert = () =>
+ createWrapper(document.querySelector('[data-testid="alert-warning"]'));
+ // Helpers
+ const renderDiagrams = () => {
+ renderMermaid([...document.querySelectorAll('.js-render-mermaid')]);
jest.runAllTimers();
- expect(document.querySelector('pre.js-syntax-highlight').classList).toContain('gl-sr-only');
+ };
+
+ beforeEach(() => {
+ document.body.dataset.page = '';
+ });
+ afterEach(() => {
resetHTMLFixture();
});
+
+ it('renders a mermaid diagram', () => {
+ setHTMLFixture('<pre><code class="js-render-mermaid"></code></pre>');
+
+ expect(findMermaidIframes()).toHaveLength(0);
+
+ renderDiagrams();
+
+ expect(document.querySelector('pre').classList).toContain('gl-sr-only');
+ expect(findMermaidIframes()).toHaveLength(1);
+ });
+
+ describe('within a details element', () => {
+ beforeEach(() => {
+ setHTMLFixture('<details><pre><code class="js-render-mermaid"></code></pre></details>');
+ renderDiagrams();
+ });
+
+ it('does not render the diagram on load', () => {
+ expect(findMermaidIframes()).toHaveLength(0);
+ });
+
+ it('render the diagram when the details element is opened', () => {
+ document.querySelector('details').setAttribute('open', true);
+ document.querySelector('details').dispatchEvent(new Event('toggle'));
+ jest.runAllTimers();
+
+ expect(findMermaidIframes()).toHaveLength(1);
+ });
+ });
+
+ describe('dangerous diagrams', () => {
+ describe(`when the diagram's source exceeds ${MAX_CHAR_LIMIT} characters`, () => {
+ beforeEach(() => {
+ setHTMLFixture(
+ `<pre>
+ <code class="js-render-mermaid">${Array(MAX_CHAR_LIMIT + 1)
+ .fill('a')
+ .join('')}</code>
+ </pre>`,
+ );
+ renderDiagrams();
+ });
+ it('does not render the diagram on load', () => {
+ expect(findMermaidIframes()).toHaveLength(0);
+ });
+
+ it('shows a warning about performance impact when rendering the diagram', () => {
+ expect(document.querySelector('pre').classList).toContain(LAZY_ALERT_SHOWN_CLASS);
+ expect(findDangerousMermaidAlert().exists()).toBe(true);
+ expect(findDangerousMermaidAlert().text()).toContain(
+ __('Warning: Displaying this diagram might cause performance issues on this page.'),
+ );
+ });
+
+ it("renders the diagram when clicking on the alert's button", () => {
+ findDangerousMermaidAlert().find('button').trigger('click');
+ jest.runAllTimers();
+
+ expect(findMermaidIframes()).toHaveLength(1);
+ });
+ });
+
+ it(`stops rendering diagrams once the total rendered source exceeds ${MAX_CHAR_LIMIT} characters`, () => {
+ setHTMLFixture(
+ `<pre>
+ <code class="js-render-mermaid">${Array(MAX_CHAR_LIMIT - 1)
+ .fill('a')
+ .join('')}</code>
+ <code class="js-render-mermaid">2</code>
+ <code class="js-render-mermaid">3</code>
+ <code class="js-render-mermaid">4</code>
+ </pre>`,
+ );
+ renderDiagrams();
+
+ expect(findMermaidIframes()).toHaveLength(3);
+ });
+
+ // Note: The test case below is provided for convenience but should remain skipped as the DOM
+ // operations it requires are too expensive and would significantly slow down the test suite.
+ // eslint-disable-next-line jest/no-disabled-tests
+ it.skip(`stops rendering diagrams when the rendered diagrams count exceeds ${MAX_MERMAID_BLOCK_LIMIT}`, () => {
+ setHTMLFixture(
+ `<pre>
+ ${Array(MAX_MERMAID_BLOCK_LIMIT + 1)
+ .fill('<code class="js-render-mermaid"></code>')
+ .join('')}
+ </pre>`,
+ );
+ renderDiagrams();
+
+ expect([...document.querySelectorAll('.js-render-mermaid')]).toHaveLength(
+ MAX_MERMAID_BLOCK_LIMIT + 1,
+ );
+ expect(findMermaidIframes()).toHaveLength(MAX_MERMAID_BLOCK_LIMIT);
+ });
+ });
});
diff --git a/spec/frontend/issues/list/components/issues_list_app_spec.js b/spec/frontend/issues/list/components/issues_list_app_spec.js
index 5133c02b190..16631752d6d 100644
--- a/spec/frontend/issues/list/components/issues_list_app_spec.js
+++ b/spec/frontend/issues/list/components/issues_list_app_spec.js
@@ -131,7 +131,6 @@ describe('CE IssuesListApp component', () => {
const mountComponent = ({
provide = {},
data = {},
- workItems = false,
issuesQueryResponse = mockIssuesQueryResponse,
issuesCountsQueryResponse = mockIssuesCountsQueryResponse,
sortPreferenceMutationResponse = jest.fn().mockResolvedValue(setSortPreferenceMutationResponse),
@@ -150,9 +149,6 @@ describe('CE IssuesListApp component', () => {
apolloProvider: createMockApollo(requestHandlers),
router,
provide: {
- glFeatures: {
- workItems,
- },
...defaultProvide,
...provide,
},
@@ -1060,45 +1056,23 @@ describe('CE IssuesListApp component', () => {
});
describe('fetching issues', () => {
- describe('when work_items feature flag is disabled', () => {
- beforeEach(() => {
- wrapper = mountComponent({ workItems: false });
- jest.runOnlyPendingTimers();
- });
-
- it('fetches issue, incident, and test case types', () => {
- const types = [
- WORK_ITEM_TYPE_ENUM_ISSUE,
- WORK_ITEM_TYPE_ENUM_INCIDENT,
- WORK_ITEM_TYPE_ENUM_TEST_CASE,
- ];
-
- expect(mockIssuesQueryResponse).toHaveBeenCalledWith(expect.objectContaining({ types }));
- expect(mockIssuesCountsQueryResponse).toHaveBeenCalledWith(
- expect.objectContaining({ types }),
- );
- });
+ beforeEach(() => {
+ wrapper = mountComponent();
+ jest.runOnlyPendingTimers();
});
- describe('when work_items feature flag is enabled', () => {
- beforeEach(() => {
- wrapper = mountComponent({ workItems: true });
- jest.runOnlyPendingTimers();
- });
-
- it('fetches issue, incident, test case, and task types', () => {
- const types = [
- WORK_ITEM_TYPE_ENUM_ISSUE,
- WORK_ITEM_TYPE_ENUM_INCIDENT,
- WORK_ITEM_TYPE_ENUM_TEST_CASE,
- WORK_ITEM_TYPE_ENUM_TASK,
- ];
-
- expect(mockIssuesQueryResponse).toHaveBeenCalledWith(expect.objectContaining({ types }));
- expect(mockIssuesCountsQueryResponse).toHaveBeenCalledWith(
- expect.objectContaining({ types }),
- );
- });
+ it('fetches issue, incident, test case, and task types', () => {
+ const types = [
+ WORK_ITEM_TYPE_ENUM_ISSUE,
+ WORK_ITEM_TYPE_ENUM_INCIDENT,
+ WORK_ITEM_TYPE_ENUM_TEST_CASE,
+ WORK_ITEM_TYPE_ENUM_TASK,
+ ];
+
+ expect(mockIssuesQueryResponse).toHaveBeenCalledWith(expect.objectContaining({ types }));
+ expect(mockIssuesCountsQueryResponse).toHaveBeenCalledWith(
+ expect.objectContaining({ types }),
+ );
});
});
});
diff --git a/spec/frontend/notebook/cells/markdown_spec.js b/spec/frontend/notebook/cells/markdown_spec.js
index c757b55faf4..a7776bd5b69 100644
--- a/spec/frontend/notebook/cells/markdown_spec.js
+++ b/spec/frontend/notebook/cells/markdown_spec.js
@@ -5,20 +5,22 @@ import markdownTableJson from 'test_fixtures/blob/notebook/markdown-table.json';
import basicJson from 'test_fixtures/blob/notebook/basic.json';
import mathJson from 'test_fixtures/blob/notebook/math.json';
import MarkdownComponent from '~/notebook/cells/markdown.vue';
+import Prompt from '~/notebook/cells/prompt.vue';
const Component = Vue.extend(MarkdownComponent);
window.katex = katex;
-function buildCellComponent(cell, relativePath = '') {
+function buildCellComponent(cell, relativePath = '', hidePrompt) {
return mount(Component, {
propsData: {
cell,
+ hidePrompt,
},
provide: {
relativeRawPath: relativePath,
},
- }).vm;
+ });
}
function buildMarkdownComponent(markdownContent, relativePath = '') {
@@ -33,7 +35,7 @@ function buildMarkdownComponent(markdownContent, relativePath = '') {
}
describe('Markdown component', () => {
- let vm;
+ let wrapper;
let cell;
let json;
@@ -43,21 +45,30 @@ describe('Markdown component', () => {
// eslint-disable-next-line prefer-destructuring
cell = json.cells[1];
- vm = buildCellComponent(cell);
+ wrapper = buildCellComponent(cell);
await nextTick();
});
- it('does not render prompt', () => {
- expect(vm.$el.querySelector('.prompt span')).toBeNull();
+ const findPrompt = () => wrapper.findComponent(Prompt);
+
+ it('renders a prompt by default', () => {
+ expect(findPrompt().exists()).toBe(true);
+ });
+
+ it('does not render a prompt if hidePrompt is true', () => {
+ wrapper = buildCellComponent(cell, '', true);
+ expect(findPrompt().exists()).toBe(false);
});
it('does not render the markdown text', () => {
- expect(vm.$el.querySelector('.markdown').innerHTML.trim()).not.toEqual(cell.source.join(''));
+ expect(wrapper.vm.$el.querySelector('.markdown').innerHTML.trim()).not.toEqual(
+ cell.source.join(''),
+ );
});
it('renders the markdown HTML', () => {
- expect(vm.$el.querySelector('.markdown h1')).not.toBeNull();
+ expect(wrapper.vm.$el.querySelector('.markdown h1')).not.toBeNull();
});
it('sanitizes Markdown output', async () => {
@@ -68,11 +79,11 @@ describe('Markdown component', () => {
});
await nextTick();
- expect(vm.$el.querySelector('a').getAttribute('href')).toBeNull();
+ expect(wrapper.vm.$el.querySelector('a').getAttribute('href')).toBeNull();
});
it('sanitizes HTML', async () => {
- const findLink = () => vm.$el.querySelector('.xss-link');
+ const findLink = () => wrapper.vm.$el.querySelector('.xss-link');
Object.assign(cell, {
source: ['<a href="test.js" data-remote=true data-type="script" class="xss-link">XSS</a>\n'],
});
@@ -97,11 +108,11 @@ describe('Markdown component', () => {
["for embedded images, it doesn't", '![](data:image/jpeg;base64)\n', 'src="data:'],
["for images urls, it doesn't", '![](http://image.png)\n', 'src="http:'],
])('%s', async ([testMd, mustContain]) => {
- vm = buildMarkdownComponent([testMd], '/raw/');
+ wrapper = buildMarkdownComponent([testMd], '/raw/');
await nextTick();
- expect(vm.$el.innerHTML).toContain(mustContain);
+ expect(wrapper.vm.$el.innerHTML).toContain(mustContain);
});
});
@@ -111,13 +122,13 @@ describe('Markdown component', () => {
});
it('renders images and text', async () => {
- vm = buildCellComponent(json.cells[0]);
+ wrapper = buildCellComponent(json.cells[0]);
await nextTick();
- const images = vm.$el.querySelectorAll('img');
+ const images = wrapper.vm.$el.querySelectorAll('img');
expect(images.length).toBe(5);
- const columns = vm.$el.querySelectorAll('td');
+ const columns = wrapper.vm.$el.querySelectorAll('td');
expect(columns.length).toBe(6);
expect(columns[0].textContent).toEqual('Hello ');
@@ -141,81 +152,93 @@ describe('Markdown component', () => {
});
it('renders multi-line katex', async () => {
- vm = buildCellComponent(json.cells[0]);
+ wrapper = buildCellComponent(json.cells[0]);
await nextTick();
- expect(vm.$el.querySelector('.katex')).not.toBeNull();
+ expect(wrapper.vm.$el.querySelector('.katex')).not.toBeNull();
});
it('renders inline katex', async () => {
- vm = buildCellComponent(json.cells[1]);
+ wrapper = buildCellComponent(json.cells[1]);
await nextTick();
- expect(vm.$el.querySelector('p:first-child .katex')).not.toBeNull();
+ expect(wrapper.vm.$el.querySelector('p:first-child .katex')).not.toBeNull();
});
it('renders multiple inline katex', async () => {
- vm = buildCellComponent(json.cells[1]);
+ wrapper = buildCellComponent(json.cells[1]);
await nextTick();
- expect(vm.$el.querySelectorAll('p:nth-child(2) .katex')).toHaveLength(4);
+ expect(wrapper.vm.$el.querySelectorAll('p:nth-child(2) .katex')).toHaveLength(4);
});
it('output cell in case of katex error', async () => {
- vm = buildMarkdownComponent(['Some invalid $a & b$ inline formula $b & c$\n', '\n']);
+ wrapper = buildMarkdownComponent(['Some invalid $a & b$ inline formula $b & c$\n', '\n']);
await nextTick();
// expect one paragraph with no katex formula in it
- expect(vm.$el.querySelectorAll('p')).toHaveLength(1);
- expect(vm.$el.querySelectorAll('p .katex')).toHaveLength(0);
+ expect(wrapper.vm.$el.querySelectorAll('p')).toHaveLength(1);
+ expect(wrapper.vm.$el.querySelectorAll('p .katex')).toHaveLength(0);
});
it('output cell and render remaining formula in case of katex error', async () => {
- vm = buildMarkdownComponent([
+ wrapper = buildMarkdownComponent([
'An invalid $a & b$ inline formula and a vaild one $b = c$\n',
'\n',
]);
await nextTick();
// expect one paragraph with no katex formula in it
- expect(vm.$el.querySelectorAll('p')).toHaveLength(1);
- expect(vm.$el.querySelectorAll('p .katex')).toHaveLength(1);
+ expect(wrapper.vm.$el.querySelectorAll('p')).toHaveLength(1);
+ expect(wrapper.vm.$el.querySelectorAll('p .katex')).toHaveLength(1);
});
it('renders math formula in list object', async () => {
- vm = buildMarkdownComponent(["- list with inline $a=2$ inline formula $a' + b = c$\n", '\n']);
+ wrapper = buildMarkdownComponent([
+ "- list with inline $a=2$ inline formula $a' + b = c$\n",
+ '\n',
+ ]);
await nextTick();
// expect one list with a katex formula in it
- expect(vm.$el.querySelectorAll('li')).toHaveLength(1);
- expect(vm.$el.querySelectorAll('li .katex')).toHaveLength(2);
+ expect(wrapper.vm.$el.querySelectorAll('li')).toHaveLength(1);
+ expect(wrapper.vm.$el.querySelectorAll('li .katex')).toHaveLength(2);
});
it("renders math formula with tick ' in it", async () => {
- vm = buildMarkdownComponent(["- list with inline $a=2$ inline formula $a' + b = c$\n", '\n']);
+ wrapper = buildMarkdownComponent([
+ "- list with inline $a=2$ inline formula $a' + b = c$\n",
+ '\n',
+ ]);
await nextTick();
// expect one list with a katex formula in it
- expect(vm.$el.querySelectorAll('li')).toHaveLength(1);
- expect(vm.$el.querySelectorAll('li .katex')).toHaveLength(2);
+ expect(wrapper.vm.$el.querySelectorAll('li')).toHaveLength(1);
+ expect(wrapper.vm.$el.querySelectorAll('li .katex')).toHaveLength(2);
});
it('renders math formula with less-than-operator < in it', async () => {
- vm = buildMarkdownComponent(['- list with inline $a=2$ inline formula $a + b < c$\n', '\n']);
+ wrapper = buildMarkdownComponent([
+ '- list with inline $a=2$ inline formula $a + b < c$\n',
+ '\n',
+ ]);
await nextTick();
// expect one list with a katex formula in it
- expect(vm.$el.querySelectorAll('li')).toHaveLength(1);
- expect(vm.$el.querySelectorAll('li .katex')).toHaveLength(2);
+ expect(wrapper.vm.$el.querySelectorAll('li')).toHaveLength(1);
+ expect(wrapper.vm.$el.querySelectorAll('li .katex')).toHaveLength(2);
});
it('renders math formula with greater-than-operator > in it', async () => {
- vm = buildMarkdownComponent(['- list with inline $a=2$ inline formula $a + b > c$\n', '\n']);
+ wrapper = buildMarkdownComponent([
+ '- list with inline $a=2$ inline formula $a + b > c$\n',
+ '\n',
+ ]);
await nextTick();
// expect one list with a katex formula in it
- expect(vm.$el.querySelectorAll('li')).toHaveLength(1);
- expect(vm.$el.querySelectorAll('li .katex')).toHaveLength(2);
+ expect(wrapper.vm.$el.querySelectorAll('li')).toHaveLength(1);
+ expect(wrapper.vm.$el.querySelectorAll('li .katex')).toHaveLength(2);
});
});
});
diff --git a/spec/frontend/notebook/cells/output/index_spec.js b/spec/frontend/notebook/cells/output/index_spec.js
index 8bf049235a9..585cbb68eeb 100644
--- a/spec/frontend/notebook/cells/output/index_spec.js
+++ b/spec/frontend/notebook/cells/output/index_spec.js
@@ -1,12 +1,15 @@
import { mount } from '@vue/test-utils';
import json from 'test_fixtures/blob/notebook/basic.json';
import Output from '~/notebook/cells/output/index.vue';
+import MarkdownOutput from '~/notebook/cells/output/markdown.vue';
+import { relativeRawPath, markdownCellContent } from '../../mock_data';
describe('Output component', () => {
let wrapper;
const createComponent = (output) => {
wrapper = mount(Output, {
+ provide: { relativeRawPath },
propsData: {
outputs: [].concat(output),
count: 1,
@@ -95,6 +98,17 @@ describe('Output component', () => {
});
});
+ describe('Markdown output', () => {
+ beforeEach(() => {
+ const markdownType = { data: { 'text/markdown': markdownCellContent } };
+ createComponent(markdownType);
+ });
+
+ it('renders a markdown component', () => {
+ expect(wrapper.findComponent(MarkdownOutput).props('rawCode')).toBe(markdownCellContent);
+ });
+ });
+
describe('default to plain text', () => {
beforeEach(() => {
const unknownType = json.cells[6];
diff --git a/spec/frontend/notebook/cells/output/markdown_spec.js b/spec/frontend/notebook/cells/output/markdown_spec.js
new file mode 100644
index 00000000000..e3490ed3bea
--- /dev/null
+++ b/spec/frontend/notebook/cells/output/markdown_spec.js
@@ -0,0 +1,44 @@
+import { mount } from '@vue/test-utils';
+import MarkdownOutput from '~/notebook/cells/output/markdown.vue';
+import Prompt from '~/notebook/cells/prompt.vue';
+import Markdown from '~/notebook/cells/markdown.vue';
+import { relativeRawPath, markdownCellContent } from '../../mock_data';
+
+describe('markdown output cell', () => {
+ let wrapper;
+
+ const createComponent = ({ count = 0, index = 0 } = {}) => {
+ wrapper = mount(MarkdownOutput, {
+ provide: { relativeRawPath },
+ propsData: {
+ rawCode: markdownCellContent,
+ count,
+ index,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ const findPrompt = () => wrapper.findComponent(Prompt);
+ const findMarkdown = () => wrapper.findComponent(Markdown);
+
+ it.each`
+ index | count | showOutput
+ ${0} | ${1} | ${true}
+ ${1} | ${2} | ${false}
+ ${2} | ${3} | ${false}
+ `('renders a prompt', ({ index, count, showOutput }) => {
+ createComponent({ count, index });
+ expect(findPrompt().props()).toMatchObject({ count, showOutput, type: 'Out' });
+ });
+
+ it('renders a Markdown component', () => {
+ expect(findMarkdown().props()).toMatchObject({
+ cell: { source: markdownCellContent },
+ hidePrompt: true,
+ });
+ });
+});
diff --git a/spec/frontend/notebook/mock_data.js b/spec/frontend/notebook/mock_data.js
new file mode 100644
index 00000000000..b1419e1256f
--- /dev/null
+++ b/spec/frontend/notebook/mock_data.js
@@ -0,0 +1,2 @@
+export const relativeRawPath = '/test';
+export const markdownCellContent = ['# Test'];
diff --git a/spec/frontend/releases/components/asset_links_form_spec.js b/spec/frontend/releases/components/asset_links_form_spec.js
index 1ff5766b074..b1e9d8d1256 100644
--- a/spec/frontend/releases/components/asset_links_form_spec.js
+++ b/spec/frontend/releases/components/asset_links_form_spec.js
@@ -292,6 +292,42 @@ describe('Release edit component', () => {
});
});
+ describe('remove button state', () => {
+ describe('when there is only one link', () => {
+ beforeEach(() => {
+ factory({
+ release: {
+ ...release,
+ assets: {
+ links: release.assets.links.slice(0, 1),
+ },
+ },
+ });
+ });
+
+ it('remove asset link button should not be present', () => {
+ expect(wrapper.find('.remove-button').exists()).toBe(false);
+ });
+ });
+
+ describe('when there are multiple links', () => {
+ beforeEach(() => {
+ factory({
+ release: {
+ ...release,
+ assets: {
+ links: release.assets.links.slice(0, 2),
+ },
+ },
+ });
+ });
+
+ it('remove asset link button should be visible', () => {
+ expect(wrapper.find('.remove-button').exists()).toBe(true);
+ });
+ });
+ });
+
describe('empty state', () => {
describe('when the release fetched from the API has no links', () => {
beforeEach(() => {
@@ -325,12 +361,6 @@ describe('Release edit component', () => {
it('does not call the addEmptyAssetLink store method when the component is created', () => {
expect(actions.addEmptyAssetLink).not.toHaveBeenCalled();
});
-
- it('calls addEmptyAssetLink when the final link is deleted by the user', () => {
- wrapper.find('.remove-button').vm.$emit('click');
-
- expect(actions.addEmptyAssetLink).toHaveBeenCalledTimes(1);
- });
});
});
});