blob: 976e85c1a46c60e69b9c06c9fd26256a474242fe [file] [log] [blame]
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001// Copyright 2019 The Chromium Authors
Copybara854996b2021-09-07 19:36:02 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import {assert, expect} from 'chai';
6import {MrAttachment} from './mr-attachment.js';
7import {prpcClient} from 'prpc-client-instance.js';
8import {FILE_DOWNLOAD_WARNING} from 'shared/settings.js';
9
10let element;
11
12describe('mr-attachment', () => {
13 beforeEach(() => {
14 element = document.createElement('mr-attachment');
15 document.body.appendChild(element);
16 sinon.stub(prpcClient, 'call').returns(Promise.resolve({}));
17 });
18
19 afterEach(() => {
20 document.body.removeChild(element);
21 prpcClient.call.restore();
22 });
23
24 it('initializes', () => {
25 assert.instanceOf(element, MrAttachment);
26 });
27
28 it('shows image thumbnail', async () => {
29 element.attachment = {
30 thumbnailUrl: 'thumbnail.jpeg',
31 contentType: 'image/jpeg',
32 };
33 await element.updateComplete;
34 const img = element.shadowRoot.querySelector('img');
35 assert.isNotNull(img);
36 assert.isTrue(img.src.endsWith('thumbnail.jpeg'));
37 });
38
39 it('shows video thumbnail', async () => {
40 element.attachment = {
41 viewUrl: 'video.mp4',
42 contentType: 'video/mpeg',
43 };
44 await element.updateComplete;
45 const video = element.shadowRoot.querySelector('video');
46 assert.isNotNull(video);
47 assert.isTrue(video.src.endsWith('video.mp4'));
48 });
49
50 it('does not show image thumbnail if deleted', async () => {
51 element.attachment = {
52 thumbnailUrl: 'thumbnail.jpeg',
53 contentType: 'image/jpeg',
54 isDeleted: true,
55 };
56 await element.updateComplete;
57 const img = element.shadowRoot.querySelector('img');
58 assert.isNull(img);
59 });
60
61 it('does not show video thumbnail if deleted', async () => {
62 element.attachment = {
63 viewUrl: 'video.mp4',
64 contentType: 'video/mpeg',
65 isDeleted: true,
66 };
67 await element.updateComplete;
68 const video = element.shadowRoot.querySelector('video');
69 assert.isNull(video);
70 });
71
72 it('deletes attachment', async () => {
73 prpcClient.call.callsFake(() => Promise.resolve({}));
74
75 element.attachment = {
76 attachmentId: 67890,
77 isDeleted: false,
78 };
79 element.canDelete = true;
80 element.projectName = 'proj';
81 element.localId = 1234;
82 element.sequenceNum = 3;
83 await element.updateComplete;
84
85 const deleteButton = element.shadowRoot.querySelector('chops-button');
86 deleteButton.click();
87
88 assert.deepEqual(prpcClient.call.getCall(0).args, [
89 'monorail.Issues', 'DeleteAttachment',
90 {
91 issueRef: {
92 projectName: 'proj',
93 localId: 1234,
94 },
95 sequenceNum: 3,
96 attachmentId: 67890,
97 delete: true,
98 },
99 ]);
100 assert.isTrue(prpcClient.call.calledOnce);
101 });
102
103 it('undeletes attachment', async () => {
104 prpcClient.call.callsFake(() => Promise.resolve({}));
105 element.attachment = {
106 attachmentId: 67890,
107 isDeleted: true,
108 };
109 element.canDelete = true;
110 element.projectName = 'proj';
111 element.localId = 1234;
112 element.sequenceNum = 3;
113 await element.updateComplete;
114
115 const deleteButton = element.shadowRoot.querySelector('chops-button');
116 deleteButton.click();
117
118 assert.deepEqual(prpcClient.call.getCall(0).args, [
119 'monorail.Issues', 'DeleteAttachment',
120 {
121 issueRef: {
122 projectName: 'proj',
123 localId: 1234,
124 },
125 sequenceNum: 3,
126 attachmentId: 67890,
127 delete: false,
128 },
129 ]);
130 assert.isTrue(prpcClient.call.calledOnce);
131 });
132
133 it('view link is not displayed if not given', async () => {
134 element.attachment = {};
135 await element.updateComplete;
136 const viewLink = element.shadowRoot.querySelector('.attachment-view');
137 assert.isNull(viewLink);
138 });
139
140 it('view link is displayed if given', async () => {
141 element.attachment = {
142 viewUrl: 'http://example.com/attachment.foo',
143 };
144 await element.updateComplete;
145 const viewLink = element.shadowRoot.querySelector('.attachment-view');
146 assert.isNotNull(viewLink);
147 expect(viewLink).to.be.displayed;
148 assert.equal(viewLink.href, 'http://example.com/attachment.foo');
149 });
150
151 describe('download', () => {
152 let downloadLink;
153
154 beforeEach(async () => {
155 sinon.stub(window, 'confirm').returns(false);
156
157
158 element.attachment = {};
159 await element.updateComplete;
160 downloadLink = element.shadowRoot.querySelector('.attachment-download');
161 // Prevent Karma from opening up new tabs because of simulated link
162 // clicks.
163 downloadLink.removeAttribute('target');
164 });
165
166 afterEach(() => {
167 window.confirm.restore();
168 });
169
170 it('download link is not displayed if not given', async () => {
171 element.attachment = {};
172 await element.updateComplete;
173 assert.isTrue(downloadLink.hidden);
174 });
175
176 it('download link is displayed if given', async () => {
177 element.attachment = {
178 downloadUrl: 'http://example.com/attachment.foo',
179 };
180 await element.updateComplete;
181 const downloadLink = element.shadowRoot.querySelector(
182 '.attachment-download');
183 assert.isFalse(downloadLink.hidden);
184 expect(downloadLink).to.be.displayed;
185 assert.equal(downloadLink.href, 'http://example.com/attachment.foo');
186 });
187
188 it('download allows recognized file extension and type', async () => {
189 element.attachment = {
190 contentType: 'image/png',
191 filename: 'not-a-virus.png',
192 downloadUrl: '#',
193 };
194 await element.updateComplete;
195
196 downloadLink.click();
197
198 sinon.assert.notCalled(window.confirm);
199 });
200
201 it('file extension matching is case insensitive', async () => {
202 element.attachment = {
203 contentType: 'image/png',
204 filename: 'not-a-virus.PNG',
205 downloadUrl: '#',
206 };
207 await element.updateComplete;
208
209 downloadLink.click();
210
211 sinon.assert.notCalled(window.confirm);
212 });
213
214 it('download warns on unrecognized file extension and type', async () => {
215 element.attachment = {
216 contentType: 'application/virus',
217 filename: 'fake-virus.exe',
218 downloadUrl: '#',
219 };
220 await element.updateComplete;
221
222 downloadLink.click();
223
224 sinon.assert.calledOnce(window.confirm);
225 sinon.assert.calledWith(window.confirm, FILE_DOWNLOAD_WARNING);
226 });
227 });
228});