Skip to content
This repository has been archived by the owner on May 22, 2021. It is now read-only.

Commit

Permalink
Merge pull request #766 from mozilla/frontend-tests
Browse files Browse the repository at this point in the history
Some frontend unit tests [WIP]
  • Loading branch information
dannycoates committed Feb 26, 2018
2 parents d241267 + fd2dfcc commit e79bacd
Show file tree
Hide file tree
Showing 26 changed files with 3,690 additions and 943 deletions.
3 changes: 2 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ assets
docs
public
test
coverage
coverage
.nyc_output
3 changes: 0 additions & 3 deletions .nsprc

This file was deleted.

1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
dist
assets/*.js
coverage
116 changes: 51 additions & 65 deletions app/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,15 @@ export async function setPassword(id, owner_token, keychain) {
return response.ok;
}

export function uploadFile(encrypted, metadata, verifierB64, keychain) {
export function uploadFile(
encrypted,
metadata,
verifierB64,
keychain,
onprogress
) {
const xhr = new XMLHttpRequest();
const upload = {
onprogress: function() {},
cancel: function() {
xhr.abort();
},
Expand Down Expand Up @@ -122,7 +127,7 @@ export function uploadFile(encrypted, metadata, verifierB64, keychain) {
fd.append('data', blob);
xhr.upload.addEventListener('progress', function(event) {
if (event.lengthComputable) {
upload.onprogress([event.loaded, event.total]);
onprogress([event.loaded, event.total]);
}
});
xhr.open('post', '/api/upload', true);
Expand All @@ -132,82 +137,63 @@ export function uploadFile(encrypted, metadata, verifierB64, keychain) {
return upload;
}

function download(id, keychain) {
function download(id, keychain, onprogress, canceller) {
const xhr = new XMLHttpRequest();
const download = {
onprogress: function() {},
cancel: function() {
xhr.abort();
},
result: new Promise(async function(resolve, reject) {
xhr.addEventListener('loadend', function() {
const authHeader = xhr.getResponseHeader('WWW-Authenticate');
if (authHeader) {
keychain.nonce = parseNonce(authHeader);
}
if (xhr.status === 404) {
return reject(new Error('notfound'));
}
if (xhr.status !== 200) {
return reject(new Error(xhr.status));
}

const blob = new Blob([xhr.response]);
const fileReader = new FileReader();
fileReader.readAsArrayBuffer(blob);
fileReader.onload = function() {
resolve(this.result);
};
});
xhr.addEventListener('progress', function(event) {
if (event.lengthComputable && event.target.status === 200) {
download.onprogress([event.loaded, event.total]);
}
});
const auth = await keychain.authHeader();
xhr.open('get', `/api/download/${id}`);
xhr.setRequestHeader('Authorization', auth);
xhr.responseType = 'blob';
xhr.send();
})
canceller.oncancel = function() {
xhr.abort();
};
return new Promise(async function(resolve, reject) {
xhr.addEventListener('loadend', function() {
canceller.oncancel = function() {};
const authHeader = xhr.getResponseHeader('WWW-Authenticate');
if (authHeader) {
keychain.nonce = parseNonce(authHeader);
}
if (xhr.status !== 200) {
return reject(new Error(xhr.status));
}

return download;
const blob = new Blob([xhr.response]);
const fileReader = new FileReader();
fileReader.readAsArrayBuffer(blob);
fileReader.onload = function() {
resolve(this.result);
};
});
xhr.addEventListener('progress', function(event) {
if (event.lengthComputable && event.target.status === 200) {
onprogress([event.loaded, event.total]);
}
});
const auth = await keychain.authHeader();
xhr.open('get', `/api/download/${id}`);
xhr.setRequestHeader('Authorization', auth);
xhr.responseType = 'blob';
xhr.send();
});
}

async function tryDownload(id, keychain, onprogress, tries = 1) {
const dl = download(id, keychain);
dl.onprogress = onprogress;
async function tryDownload(id, keychain, onprogress, canceller, tries = 1) {
try {
const result = await dl.result;
const result = await download(id, keychain, onprogress, canceller);
return result;
} catch (e) {
if (e.message === '401' && --tries > 0) {
return tryDownload(id, keychain, onprogress, tries);
return tryDownload(id, keychain, onprogress, canceller, tries);
}
throw e;
}
}

export function downloadFile(id, keychain) {
let cancelled = false;
function updateProgress(p) {
if (cancelled) {
// This is a bit of a hack
// We piggyback off of the progress event as a chance to cancel.
// Otherwise wiring the xhr abort up while allowing retries
// gets pretty nasty.
// 'this' here is the object returned by download(id, keychain)
return this.cancel();
}
dl.onprogress(p);
export function downloadFile(id, keychain, onprogress) {
const canceller = {
oncancel: function() {} // download() sets this
};
function cancel() {
canceller.oncancel();
}
const dl = {
onprogress: function() {},
cancel: function() {
cancelled = true;
},
result: tryDownload(id, keychain, updateProgress, 2)
return {
cancel,
result: tryDownload(id, keychain, onprogress, canceller, 2)
};
return dl;
}
10 changes: 5 additions & 5 deletions app/fileManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,6 @@ export default function(state, emitter) {
const receiver = new FileReceiver(file);
try {
await receiver.getMetadata();
receiver.on('progress', updateProgress);
receiver.on('decrypting', render);
state.transfer = receiver;
} catch (e) {
if (e.message === '401') {
Expand All @@ -164,14 +162,16 @@ export default function(state, emitter) {
});

emitter.on('download', async file => {
state.transfer.on('progress', render);
state.transfer.on('progress', updateProgress);
state.transfer.on('decrypting', render);
const links = openLinksInNewTab();
const size = file.size;
try {
const start = Date.now();
metrics.startedDownload({ size: file.size, ttl: file.ttl });
await state.transfer.download();
const dl = state.transfer.download();
render();
await dl;
const time = Date.now() - start;
const speed = size / (time / 1000);
await delay(1000);
Expand All @@ -188,7 +188,7 @@ export default function(state, emitter) {
}
console.error(err);
state.transfer = null;
const location = err.message === 'notfound' ? '/404' : '/error';
const location = err.message === '404' ? '/404' : '/error';
if (location === '/error') {
state.raven.captureException(err);
metrics.stoppedDownload({ size, err });
Expand Down
61 changes: 26 additions & 35 deletions app/fileReceiver.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,64 +30,55 @@ export default class FileReceiver extends Nanobus {
}

cancel() {
this.cancelled = true;
if (this.fileDownload) {
this.fileDownload.cancel();
if (this.downloadRequest) {
this.downloadRequest.cancel();
}
}

reset() {
this.fileDownload = null;
this.msg = 'fileSizeProgress';
this.state = 'initialized';
this.progress = [0, 1];
this.cancelled = false;
}

async getMetadata() {
const meta = await metadata(this.fileInfo.id, this.keychain);
if (meta) {
this.keychain.setIV(meta.iv);
this.fileInfo.name = meta.name;
this.fileInfo.type = meta.type;
this.fileInfo.iv = meta.iv;
this.fileInfo.size = meta.size;
this.state = 'ready';
return;
}
this.state = 'invalid';
return;
this.keychain.setIV(meta.iv);
this.fileInfo.name = meta.name;
this.fileInfo.type = meta.type;
this.fileInfo.iv = meta.iv;
this.fileInfo.size = meta.size;
this.state = 'ready';
}

async download() {
async download(noSave = false) {
this.state = 'downloading';
this.emit('progress', this.progress);
try {
const download = await downloadFile(this.fileInfo.id, this.keychain);
download.onprogress = p => {
this.downloadRequest = await downloadFile(
this.fileInfo.id,
this.keychain,
p => {
this.progress = p;
this.emit('progress', p);
};
this.fileDownload = download;
const ciphertext = await download.result;
this.fileDownload = null;
this.emit('progress');
}
);
try {
const ciphertext = await this.downloadRequest.result;
this.downloadRequest = null;
this.msg = 'decryptingFile';
this.state = 'decrypting';
this.emit('decrypting');
const plaintext = await this.keychain.decryptFile(ciphertext);
if (this.cancelled) {
throw new Error(0);
if (!noSave) {
await saveFile({
plaintext,
name: decodeURIComponent(this.fileInfo.name),
type: this.fileInfo.type
});
}
await saveFile({
plaintext,
name: decodeURIComponent(this.fileInfo.name),
type: this.fileInfo.type
});
this.msg = 'downloadFinish';
this.state = 'complete';
return;
} catch (e) {
this.state = 'invalid';
this.downloadRequest = null;
throw e;
}
}
Expand Down
22 changes: 13 additions & 9 deletions app/fileSender.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,8 @@ export default class FileSender extends Nanobus {
constructor(file) {
super('FileSender');
this.file = file;
this.uploadRequest = null;
this.msg = 'importingFile';
this.progress = [0, 1];
this.cancelled = false;
this.keychain = new Keychain();
this.reset();
}

get progressRatio() {
Expand All @@ -31,6 +28,13 @@ export default class FileSender extends Nanobus {
};
}

reset() {
this.uploadRequest = null;
this.msg = 'importingFile';
this.progress = [0, 1];
this.cancelled = false;
}

cancel() {
this.cancelled = true;
if (this.uploadRequest) {
Expand Down Expand Up @@ -71,13 +75,13 @@ export default class FileSender extends Nanobus {
encrypted,
metadata,
authKeyB64,
this.keychain
this.keychain,
p => {
this.progress = p;
this.emit('progress', p);
}
);
this.msg = 'fileSizeProgress';
this.uploadRequest.onprogress = p => {
this.progress = p;
this.emit('progress', p);
};
try {
const result = await this.uploadRequest.result;
const time = Date.now() - start;
Expand Down
3 changes: 2 additions & 1 deletion app/ownedFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default class OwnedFile {
this.ownerToken = obj.ownerToken;
this.dlimit = obj.dlimit || 1;
this.dtotal = obj.dtotal || 0;
this.keychain = new Keychain(obj.secretKey);
this.keychain = new Keychain(obj.secretKey, obj.nonce);
this._hasPassword = !!obj.hasPassword;
}

Expand Down Expand Up @@ -59,6 +59,7 @@ export default class OwnedFile {
if (e.message === '404') {
this.dtotal = this.dlimit;
}
// ignore other errors
}
}

Expand Down
Loading

0 comments on commit e79bacd

Please sign in to comment.