Skip to content

Commit 7079586

Browse files
committed
Remove file-saver dependency
1 parent 726a4b9 commit 7079586

7 files changed

Lines changed: 120 additions & 100 deletions

File tree

package-lock.json

Lines changed: 0 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
"acorn-walk": "^8.3.4",
2929
"colorjs.io": "^0.6.0",
3030
"escodegen": "^2.1.0",
31-
"file-saver": "^1.3.8",
3231
"gifenc": "^1.0.3",
3332
"i18next": "^19.0.2",
3433
"i18next-browser-languagedetector": "^4.0.1",

src/io/utilities.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import * as fileSaver from 'file-saver';
2-
31
function downloadFile(data, fName, extension) {
42
const fx = _checkFileExtension(fName, extension);
53
const filename = fx[0];
@@ -9,7 +7,14 @@ function downloadFile(data, fName, extension) {
97
saveData = new Blob([data]);
108
}
119

12-
fileSaver.saveAs(saveData, filename);
10+
if(document){
11+
const url = URL.createObjectURL(saveData);
12+
const link = document.createElement('a');
13+
link.href = url;
14+
link.download = filename;
15+
link.click();
16+
URL.revokeObjectURL(url);
17+
}
1318
}
1419

1520
function _checkFileExtension(filename, extension) {

test/unit/image/downloading.js

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
import { mockP5, mockP5Prototype } from '../../js/mocks';
2-
import * as fileSaver from 'file-saver';
32
import { vi } from 'vitest';
43
import image from '../../../src/image/image';
54
import files from '../../../src/io/files';
65
import loading from '../../../src/image/loading_displaying';
76
import p5Image from '../../../src/image/p5.Image';
87

9-
vi.mock('file-saver', () => {
10-
return {
11-
saveAs: vi.fn()
12-
};
8+
const mockAnchorElement = vi.mockObject({
9+
href: null,
10+
download: null,
11+
click: () => {}
12+
});
13+
const originalCreateElement = document.createElement;
14+
vi.spyOn(document, 'createElement').mockImplementation((...args) => {
15+
if(args[0] !== 'a'){
16+
return originalCreateElement.apply(document, args);
17+
}else{
18+
return mockAnchorElement;
19+
}
1320
});
1421

1522
expect.extend({
@@ -90,12 +97,10 @@ suite('Downloading', () => {
9097

9198
test('should download a gif', async () => {
9299
mockP5Prototype.encodeAndDownloadGif(myGif);
93-
expect(fileSaver.saveAs).toHaveBeenCalledTimes(1);
94-
expect(fileSaver.saveAs)
95-
.toHaveBeenCalledWith(
96-
expect.tobeGif(),
97-
'untitled.gif'
98-
);
100+
101+
expect(document.createElement).toHaveBeenCalledTimes(1);
102+
expect(mockAnchorElement.click).toHaveBeenCalledTimes(1);
103+
assert.equal(mockAnchorElement.download, 'untitled.gif');
99104
});
100105
});
101106
});
@@ -109,34 +114,25 @@ suite('Downloading', () => {
109114
test('should download a png file', async () => {
110115
mockP5Prototype.saveCanvas();
111116
await wait(100);
112-
expect(fileSaver.saveAs).toHaveBeenCalledTimes(1);
113-
expect(fileSaver.saveAs)
114-
.toHaveBeenCalledWith(
115-
expect.tobePng(),
116-
'untitled.png'
117-
);
117+
expect(document.createElement).toHaveBeenCalledTimes(1);
118+
expect(mockAnchorElement.click).toHaveBeenCalledTimes(1);
119+
assert.equal(mockAnchorElement.download, 'untitled.png');
118120
});
119121

120122
test('should download a jpg file I', async () => {
121123
mockP5Prototype.saveCanvas('filename.jpg');
122124
await wait(100);
123-
expect(fileSaver.saveAs).toHaveBeenCalledTimes(1);
124-
expect(fileSaver.saveAs)
125-
.toHaveBeenCalledWith(
126-
expect.tobeJpg(),
127-
'filename.jpg'
128-
);
125+
expect(document.createElement).toHaveBeenCalledTimes(1);
126+
expect(mockAnchorElement.click).toHaveBeenCalledTimes(1);
127+
assert.equal(mockAnchorElement.download, 'filename.jpg');
129128
});
130129

131130
test('should download a jpg file II', async () => {
132131
mockP5Prototype.saveCanvas('filename', 'jpg');
133132
await wait(100);
134-
expect(fileSaver.saveAs).toHaveBeenCalledTimes(1);
135-
expect(fileSaver.saveAs)
136-
.toHaveBeenCalledWith(
137-
expect.tobeJpg(),
138-
'filename.jpg'
139-
);
133+
expect(document.createElement).toHaveBeenCalledTimes(1);
134+
expect(mockAnchorElement.click).toHaveBeenCalledTimes(1);
135+
assert.equal(mockAnchorElement.download, 'filename.jpg');
140136
});
141137
});
142138

@@ -192,12 +188,9 @@ suite('Downloading', () => {
192188

193189
test.todo('should download a GIF', async () => {
194190
await mockP5Prototype.saveGif('myGif', 3, 2);
195-
expect(fileSaver.saveAs).toHaveBeenCalledTimes(1);
196-
expect(fileSaver.saveAs)
197-
.toHaveBeenCalledWith(
198-
expect.tobeGif(),
199-
'myGif.gif'
200-
);
191+
expect(document.createElement).toHaveBeenCalledTimes(1);
192+
expect(mockAnchorElement.click).toHaveBeenCalledTimes(1);
193+
assert.equal(mockAnchorElement.download, 'myGif.gif');
201194
});
202195
});
203196
});

test/unit/io/files.js

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
import { mockP5, mockP5Prototype, httpMock } from '../../js/mocks';
22
import files from '../../../src/io/files';
33
import { vi } from 'vitest';
4-
import * as fileSaver from 'file-saver';
54

6-
vi.mock('file-saver', () => {
7-
return {
8-
saveAs: vi.fn()
9-
};
5+
const mockAnchorElement = vi.mockObject({
6+
href: null,
7+
download: null,
8+
click: () => {}
109
});
10+
const originalCreateElement = document.createElement;
11+
vi.spyOn(document, 'createElement').mockImplementation((...args) => {
12+
if(args[0] !== 'a'){
13+
return originalCreateElement.apply(document, args);
14+
}else{
15+
return mockAnchorElement;
16+
}
17+
});
18+
vi.spyOn(URL, 'createObjectURL');
1119

1220
suite('Files', function() {
1321
beforeAll(async function() {
@@ -58,17 +66,21 @@ suite('Files', function() {
5866
mockP5Prototype.saveStrings(strings, 'myfile');
5967

6068
const saveData = new Blob([strings.join('\n')]);
61-
expect(fileSaver.saveAs).toHaveBeenCalledTimes(1);
62-
expect(fileSaver.saveAs).toHaveBeenCalledWith(saveData, 'myfile.txt');
69+
expect(document.createElement).toHaveBeenCalledTimes(1);
70+
expect(mockAnchorElement.click).toHaveBeenCalledTimes(1);
71+
expect(URL.createObjectURL).toHaveBeenCalledWith(saveData);
72+
assert.equal(mockAnchorElement.download, 'myfile.txt');
6373
});
6474

6575
test('should download a file with expected contents with CRLF', async () => {
6676
const strings = ['some', 'words'];
6777
mockP5Prototype.saveStrings(strings, 'myfile', 'txt', true);
6878

6979
const saveData = new Blob([strings.join('\r\n')]);
70-
expect(fileSaver.saveAs).toHaveBeenCalledTimes(1);
71-
expect(fileSaver.saveAs).toHaveBeenCalledWith(saveData, 'myfile.txt');
80+
expect(document.createElement).toHaveBeenCalledTimes(1);
81+
expect(mockAnchorElement.click).toHaveBeenCalledTimes(1);
82+
expect(URL.createObjectURL).toHaveBeenCalledWith(saveData);
83+
assert.equal(mockAnchorElement.download, 'myfile.txt');
7284
});
7385
});
7486

@@ -84,8 +96,10 @@ suite('Files', function() {
8496
mockP5Prototype.saveJSON(myObj, 'myfile');
8597

8698
const saveData = new Blob([JSON.stringify(myObj, null, 2)]);
87-
expect(fileSaver.saveAs).toHaveBeenCalledTimes(1);
88-
expect(fileSaver.saveAs).toHaveBeenCalledWith(saveData, 'myfile.json');
99+
expect(document.createElement).toHaveBeenCalledTimes(1);
100+
expect(mockAnchorElement.click).toHaveBeenCalledTimes(1);
101+
expect(URL.createObjectURL).toHaveBeenCalledWith(saveData);
102+
assert.equal(mockAnchorElement.download, 'myfile.json');
89103
});
90104
});
91105

@@ -101,8 +115,10 @@ suite('Files', function() {
101115
mockP5Prototype.writeFile(myArray, 'myfile');
102116

103117
const saveData = new Blob(myArray);
104-
expect(fileSaver.saveAs).toHaveBeenCalledTimes(1);
105-
expect(fileSaver.saveAs).toHaveBeenCalledWith(saveData, 'myfile');
118+
expect(document.createElement).toHaveBeenCalledTimes(1);
119+
expect(mockAnchorElement.click).toHaveBeenCalledTimes(1);
120+
expect(URL.createObjectURL).toHaveBeenCalledWith(saveData);
121+
assert.equal(mockAnchorElement.download, 'myfile');
106122
});
107123
});
108124

@@ -119,8 +135,10 @@ suite('Files', function() {
119135
mockP5Prototype.downloadFile(inBlob, 'myfile');
120136

121137
const saveData = new Blob(myArray);
122-
expect(fileSaver.saveAs).toHaveBeenCalledTimes(1);
123-
expect(fileSaver.saveAs).toHaveBeenCalledWith(saveData, 'myfile');
138+
expect(document.createElement).toHaveBeenCalledTimes(1);
139+
expect(mockAnchorElement.click).toHaveBeenCalledTimes(1);
140+
expect(URL.createObjectURL).toHaveBeenCalledWith(saveData);
141+
assert.equal(mockAnchorElement.download, 'myfile');
124142
});
125143
});
126144

@@ -155,17 +173,21 @@ suite('Files', function() {
155173
mockP5Prototype.save(myStrings, 'filename');
156174

157175
const saveData = new Blob([myStrings.join('\n')]);
158-
expect(fileSaver.saveAs).toHaveBeenCalledTimes(1);
159-
expect(fileSaver.saveAs).toHaveBeenCalledWith(saveData, 'filename.txt');
176+
expect(document.createElement).toHaveBeenCalledTimes(1);
177+
expect(mockAnchorElement.click).toHaveBeenCalledTimes(1);
178+
expect(URL.createObjectURL).toHaveBeenCalledWith(saveData);
179+
assert.equal(mockAnchorElement.download, 'filename.txt');
160180
});
161181

162182
test('should download a json file', async () => {
163183
const myObj = { hi: 'hello' };
164184
mockP5Prototype.save(myObj, 'filename.json');
165185

166186
const saveData = new Blob([JSON.stringify(myObj, null, 2)]);
167-
expect(fileSaver.saveAs).toHaveBeenCalledTimes(1);
168-
expect(fileSaver.saveAs).toHaveBeenCalledWith(saveData, 'filename.json');
187+
expect(document.createElement).toHaveBeenCalledTimes(1);
188+
expect(mockAnchorElement.click).toHaveBeenCalledTimes(1);
189+
expect(URL.createObjectURL).toHaveBeenCalledWith(saveData);
190+
assert.equal(mockAnchorElement.download, 'filename.json');
169191
});
170192
});
171193
});

test/unit/io/saveTable.js

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
import { mockP5, mockP5Prototype } from '../../js/mocks';
2-
import * as fileSaver from 'file-saver';
32
import { vi } from 'vitest';
43
import files from '../../../src/io/files';
54
import table from '../../../src/io/p5.Table';
65
import tableRow from '../../../src/io/p5.TableRow';
76

8-
vi.mock('file-saver', () => {
9-
return {
10-
saveAs: vi.fn()
11-
};
7+
const mockAnchorElement = vi.mockObject({
8+
href: null,
9+
download: null,
10+
click: () => {}
11+
});
12+
const originalCreateElement = document.createElement;
13+
vi.spyOn(document, 'createElement').mockImplementation((...args) => {
14+
if(args[0] !== 'a'){
15+
return originalCreateElement.apply(document, args);
16+
}else{
17+
return mockAnchorElement;
18+
}
1219
});
1320

1421
suite('saveTable', function() {
@@ -35,35 +42,28 @@ suite('saveTable', function() {
3542
mockP5Prototype.saveTable(myTable, 'filename');
3643

3744
// TODO: Need comprehensive way to compare blobs in spy call
38-
expect(fileSaver.saveAs).toHaveBeenCalledTimes(1);
39-
expect(fileSaver.saveAs)
40-
.toHaveBeenCalledWith(
41-
expect.any(Blob),
42-
'filename.csv'
43-
);
45+
// REF: io/files.js
46+
expect(document.createElement).toHaveBeenCalledTimes(1);
47+
expect(mockAnchorElement.click).toHaveBeenCalledTimes(1);
48+
assert.equal(mockAnchorElement.download, 'filename.csv');
4449
});
4550

4651
test('should download a file with expected contents (tsv)', async () => {
4752
mockP5Prototype.saveTable(myTable, 'filename', 'tsv');
4853

4954
// TODO: Need comprehensive way to compare blobs in spy call
50-
expect(fileSaver.saveAs).toHaveBeenCalledTimes(1);
51-
expect(fileSaver.saveAs)
52-
.toHaveBeenCalledWith(
53-
expect.any(Blob),
54-
'filename.tsv'
55-
);
55+
// REF: io/files.js
56+
expect(document.createElement).toHaveBeenCalledTimes(1);
57+
expect(mockAnchorElement.click).toHaveBeenCalledTimes(1);
58+
assert.equal(mockAnchorElement.download, 'filename.tsv');
5659
});
5760

5861
test('should download a file with expected contents (html)', async () => {
5962
mockP5Prototype.saveTable(myTable, 'filename', 'html');
6063

61-
expect(fileSaver.saveAs).toHaveBeenCalledTimes(1);
62-
expect(fileSaver.saveAs)
63-
.toHaveBeenCalledWith(
64-
expect.any(Blob),
65-
'filename.html'
66-
);
64+
expect(document.createElement).toHaveBeenCalledTimes(1);
65+
expect(mockAnchorElement.click).toHaveBeenCalledTimes(1);
66+
assert.equal(mockAnchorElement.download, 'filename.html');
6767
});
6868
// testWithDownload(
6969
// 'should download a file with expected contents (html)',

0 commit comments

Comments
 (0)