Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(screenshot): Improve Firebase Functions for screenshot tests #3732

Merged
merged 7 commits into from
Apr 17, 2017

Conversation

tinayuangao
Copy link
Contributor

@googlebot googlebot added the cla: yes PR author has agreed to Google's Contributor License Agreement label Mar 23, 2017
@tinayuangao tinayuangao force-pushed the screenshot-functions2 branch 2 times, most recently from 8bcbdd1 to 675197f Compare March 24, 2017 23:04
@devversion
Copy link
Member

@tinayuangao Should I review this once #3628 is merged?

@tinayuangao tinayuangao force-pushed the screenshot-functions2 branch from 675197f to 16ee200 Compare March 24, 2017 23:44
@tinayuangao tinayuangao added the in progress This issue is currently in progress label Mar 24, 2017
@tinayuangao
Copy link
Contributor Author

@devversion Yes. I will remove in-progress label when it's ready.

@tinayuangao tinayuangao force-pushed the screenshot-functions2 branch 3 times, most recently from 1f67986 to dd86b5d Compare March 25, 2017 01:26
@tinayuangao tinayuangao added pr: needs review and removed in progress This issue is currently in progress labels Mar 25, 2017

/**
* Convert data to images. Image data posted to database will be saved as png files
* and upload to screenshot/$prNumber/dataType/$filename
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uploaded?

let data = event.data.val();
let saveFilename = `${event.params.filename}.screenshot.png`;

if (dataType != 'diff' && dataType != 'test') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: It is always desired to use strict comparison - dataType !== 'diff'

let result = event.data.val() == true;
let prNumber = event.params.prNumber;
return event.data.ref.parent.child('sha').once('value').then((sha: firebaseAdmin.database.DataSnapshot) => {
return setGithubStatus(sha.val(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the API we used in /tools/gulp/util/github is a bit more convenient. You just pass in a JSON config instead of X parameters.

"jsonwebtoken": "^7.3.0"
"jsonwebtoken": "^7.3.0",
"request": "^2.81.0",
"typescript": "^2.2.1"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we build the functions/*.ts file before deploying and Firebase shouldn't care about it? If so then we can just remove it from the package file here.

let keys: string[] = [];
let counter = 0;
snapshot.forEach((childSnapshot: firebaseAdmin.database.DataSnapshot) => {
if (childSnapshot.val() == false) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Strict comparsion

keys.push(childSnapshot.key);
}
counter ++;
if (counter == snapshot.numChildren()) return true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to manually cancel the enumeration? Doesn't the loop stop automatically when you iterated through all?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is.
But typescript requires return boolean, and once boolean returned the forEach loop will end.

export function copyTestImagesToGoldens(prNumber: string) {
return firebaseAdmin.database().ref(`screenshot/reports/${prNumber}/results`).once('value')
.then((snapshot: firebaseAdmin.database.DataSnapshot) => {
let keys: string[] = [];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about failedFilenames?

"jwt_util.ts",
"test_goldens.ts",
"util/github.ts",
"util/jwt.ts"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming that index.ts is your entry-point and all other files are somehow imported by it. You could just add index.ts as file her.e

description: string,
url: string,
repoSlug: string,
token: string) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned above, I would prefer the syntax / API we currently have on tools/gulp/util/github.ts

.then(() => database.goOffline(), () => database.goOffline());
}
});

function updateFileResult(database: firebase.database.Database, prNumber: string,
filenameKey: string, result: boolean) {
return getPullRequestRef(database, prNumber).child('results').child(filenameKey).set(result);
return getPullRequestRef(database, prNumber).child('results').child(filenameKey).set(result) ;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused extra space at line end?

@tinayuangao
Copy link
Contributor Author

Comments addressed. Please take another look. Thanks!

* temporary folder if the token is valid.
* Move the data to 'screenshot/reports/$prNumber/$path
*/
export function verifyJWTAndUpdateData(event: any, path: string) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

verifyJwtAndTransferResultToTrustedLocation?

@@ -0,0 +1,22 @@
import * as firebaseAdmin from 'firebase-admin';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could there be a more specific name for this file than data.ts? Maybe verify-and-copy-screenshot-report.ts or copy-trusted-screenshot-report.ts?

In general the screenshot-related files under functions/ should have screenshot in the name somewhere (since we're likely going to add more firebase functions). A screenshot-test/ subdirectory would also work if firebase supports that.

* Handle data written to temporary folder. Validate the JWT and move the data out of
* temporary folder if the token is valid.
* Move the data to 'screenshot/reports/$prNumber/$path
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  /**
   * Verifies that a screenshot report is valid (trusted via JWT) and, if so, copies it from the
   * temporary, unauthenticated location to the more permanent, trusted location.
   */

*/
export function verifyJWTAndUpdateData(event: any, path: string) {
// Only edit data when it is first created. Exit when the data is deleted.
if (event.data.previous.exists() || !event.data.exists()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to create utility functions for things like this? E.g.,

isEditEvent(event) {
  return event.data.previous.exists() && event.data.exists();
}

isDeleteEvent(event) {
  return event.data.previous.exists() && !event.data.exists();
}

isCreateEvent(event) {
  return !event.data.previous.exists() && event.data.exists();
}

* Verify event params have correct JsonWebToken, and execute callback when the JWT is verified.
* Delete the data if there's an error or the callback is done
*/
export function verifySecureTokenAndExecute(event: firebaseFunctions.Event<any>) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this returns a promise, I would just call the function verifySecureToken. That way, then calling it, it will read like

"Verify secure token, then ..."

return firebaseAdmin.database().ref().child('screenshot/reports')
.child(prNumber).child(path).set(data);
});
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How much of the overall screenshot code could be moved to tools/screenshot-testing and how much needs to live in functions/? I'd like to try to package everything under tools/ as the shareable package that can be used in other repos. Ideally the bits that live in functions/ would be very minimal.

* Convert data to images. Image data posted to database will be saved as png files
* and uploaded to screenshot/$prNumber/dataType/$filename
*/
export function convertTestImageDataToFiles(event: any) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Convert data to images"

What format is the data that's being converted?

let data = event.data.val();
let saveFilename = `${event.params.filename}.screenshot.png`;

if (dataType !== 'diff' && dataType !== 'test') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment explaining what this condition is checking for?

const jwt = require('jsonwebtoken');
const fs = require('fs');

Object.defineProperty(exports, "__esModule", { value: true });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's going on with these imports? At the very least it needs an explanatory comment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that file was accidentally committed? There is also the index.ts. @tinayuangao

var image_data_1 = require("./image_data");
var data_image_1 = require("./data_image");
var test_goldens_1 = require("./test_goldens");
var github_1 = require("./github");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer single-quotes over double-quotes.

.gitignore Outdated
@@ -8,6 +8,8 @@

# dependencies
/node_modules
/functions/node_modules
/tools/screenshot-test/functions/node_modules
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you remove the slash for the /node_modules above. Then it will include all node_modules/ folders in the whole project.

Just do

node_modules/

@tinayuangao tinayuangao force-pushed the screenshot-functions2 branch 2 times, most recently from fffb5f0 to 62b56a3 Compare April 3, 2017 18:57
if (prNumber) {

if (isTravisPushBuild()) {
// Only update goldens for build
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Only update goldens for commits to master

?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, it might make sense to rename isTravisPushBuild to isTravisMasterBuild (or invert the function and rename it to isTravisPullRequestBuild)

const bucket = gcs.bucket(firebaseFunctions.config().firebase.storageBucket);

/**
* Convert data to images. Image data posted to database will be saved as png files
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the incoming data a base-64 encoded image? If so, the comment should say something like

"Writes base-64 encoded test images to png files on the filesystem."

I'd also rename the function to writeTestImagesToFiles

@@ -0,0 +1,39 @@
import * as firebaseFunctions from 'firebase-functions';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use dashes in the filenames instead of underscores?

I would also call this file write-images.ts

/**
* Read golden files under /goldens/ and store the image data to
* database /screenshot/goldens/$filename
* Convert png image files to BufferArray data
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a base-64 encoded string?

* database /screenshot/goldens/$filename
* Convert png image files to BufferArray data
*/
export function convertGoldenImagesToData(name: string, resourceState: string, fileBucket: any) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe copyGoldImagesToDatabase?

}
counter++;
if (counter == snapshot.numChildren()) {
return true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this true is used

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here the type check requires return a boolean.

});
return failedFilenames;
}).then((failedFilenames: string[]) => {
return bucket.getFiles({prefix: `screenshots/${prNumber}/test`}).then(function (data: any) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arrow function here?

@tinayuangao tinayuangao force-pushed the screenshot-functions2 branch 2 times, most recently from 3d4887a to f1edc9e Compare April 10, 2017 18:31
@tinayuangao tinayuangao force-pushed the screenshot-functions2 branch 2 times, most recently from 8c970fc to abe107d Compare April 11, 2017 00:17
@tinayuangao tinayuangao force-pushed the screenshot-functions2 branch from abe107d to c2e8cbc Compare April 11, 2017 00:20
@jelbourn jelbourn added pr: lgtm action: merge The PR is ready for merge by the caretaker and removed pr: needs review labels Apr 11, 2017
@jelbourn
Copy link
Member

LGTM

@jelbourn jelbourn merged commit 25a6881 into angular:master Apr 17, 2017
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 6, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker cla: yes PR author has agreed to Google's Contributor License Agreement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Screenshot Testing - Firebase Functions
4 participants