Repository: jsperf/jsperf.com
Pull request #325 (2017-01-22 06:28:49)

many fixes + some ✨
- [x] use GitHub profile to populate test and comment forms (no more local storage)
- [x] show logged in with GitHub
- [x] fix url used when creating revisions in browserscope
- [x] allow test to be deleted if its title and code are empty and updating own page
- [x] fix editing your own tests (no longer inserts new tests)
- [x] "My tests" link now works


maxbeatty: e2e tests are failing inconsistently on Saucelabs but not locally 😑
maxbeatty: @mathiasbynens @asilluron not sure how fruitful battling Saucelabs will be. if you're good with these changes, maybe we relax the required CI checks so this can get live and fix a lot of people's frustrations?
asilluron: For sure. I'm fine with that.


On Mon, Jan 23, 2017 at 10:44 AM Max Beatty <notifications@github.com>
wrote:

> @mathiasbynens <https://github.com/mathiasbynens> @asilluron
> <https://github.com/asilluron> not sure how fruitful battling Saucelabs
> will be. if you're good with these changes, maybe we relax the required CI
> checks so this can get live and fix a lot of people's frustrations?
>
>
>
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/jsperf/jsperf.com/pull/325#issuecomment-274579056>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AB2PsSliiuL2jDZ7OnuYYZKXfjmSJrasks5rVPUegaJpZM4LqS9t>
> .
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

mathiasbynens: SGTM. Fixing the issues would be a great step forward, regardless of what Saucelabs thinks about this PR ;)

Patch:
diff --git a/client/ui.js b/client/ui.js
index 7c9d5bd..09bf355 100644
--- a/client/ui.js
+++ b/client/ui.js
@@ -222,17 +222,6 @@
         setHTML('run', texts.run.ready);
         setStatus(texts.status.ready);
 
-        // prefill author details (only for pages with a comment form)
-        var $author = $('author');
-        if ($author && has.localStorage) {
-          _.each([$author, $('authorEmail'), $('authorURL')], function (element) {
-            element.value = localStorage[element.id] || '';
-            element.oninput = element.onkeydown = function (event) {
-              event && event.type < 'k' && (element.onkeydown = null);
-              localStorage[element.id] = element.value;
-            };
-          });
-        }
         // show warning when Firebug is enabled (avoids showing for Firebug Lite)
         try {
           // Firebug 1.9 no longer has `console.firebug`
diff --git a/public/_js/main.src.js b/public/_js/main.src.js
index 70edbcd..3f7902c 100644
--- a/public/_js/main.src.js
+++ b/public/_js/main.src.js
@@ -70,7 +70,6 @@ $(function() {
 	    $addExt = $('<button id="add-ext" title="Ext Core v3.x">Ext Core</button>').addScript('ext-core/3/ext-core'),
 	    $addMyLib = $('<button id="add-mylib" title="My Library v0.99">My Library</button>').addScript('mylib099-min', true),
 	    $addDiv = $('<div id="add-buttons" />'),
-	    storage = (function() { try { var storage = window.localStorage; return storage.getItem && storage; } catch(e) {} }()),
 	    $authorFields;
 
 	// http://jsperf.com/slugs
@@ -78,17 +77,6 @@ $(function() {
 		return str.toLowerCase().match(/[a-z0-9]+/ig).join('-');
 	}
 
-	if (storage) {
-		$authorFields = $('#author, #authorEmail, #authorURL');
-		if ($authorFields.length == $authorFields.filter(function() { return !this.value; }).length) {
-			$authorFields.each(function() {
-				this.value = storage[this.id] || '';
-			}).input(function() {
-				storage[this.id] = this.value;
-			});
-		}
-	}
-
 	$addTest.click(function(event) {
 		$(testHTML.replace(/1/g, ++testCount)).appendTo($tests);
 		event.preventDefault();
diff --git a/server/lib/schema.js b/server/lib/schema.js
index 5eb184f..224defb 100644
--- a/server/lib/schema.js
+++ b/server/lib/schema.js
@@ -21,10 +21,14 @@ exports.testPage = Joi.object().keys({
   setup: mediumText,
   teardown: mediumText,
   test: Joi.array().required().min(2).items(Joi.object().required().keys({
-    title: Joi.string().required().trim().min(1).max(255),
+    title: Joi.string().required().trim().allow('').empty('').max(255).when('code', {
+      is: '',
+      then: Joi.string().length(0), // must be blank too so can delete
+      otherwise: Joi.string().min(1)
+    }),
+    code: Joi.string().required().trim().allow('').max(defaults.mediumTextLength),
     defer: Joi.string().default('n').valid('y', 'n'),
-    code: Joi.string().required().trim().min(1).max(defaults.mediumTextLength),
-    testID: Joi.number().integer() // optional. only present when editing
+    testID: Joi.number().integer().optional() // only present when editing
   }))
 });
 
diff --git a/server/repositories/browserscope.js b/server/repositories/browserscope.js
index 80a8406..620f684 100644
--- a/server/repositories/browserscope.js
+++ b/server/repositories/browserscope.js
@@ -8,7 +8,7 @@ exports.register = function (server, options, next) {
         'api_key': options.api_key,
         name: title,
         description: description.substr(0, 60),
-        url: options.scheme + '://' + options.domain + '/' + slug
+        url: `${options.scheme}://${options.domain}/${slug}`
       });
 
       https.get('https://www.browserscope.org/user/tests/create?' + qs, function (res) {
diff --git a/server/repositories/pages.js b/server/repositories/pages.js
index 05e93fd..3ab03dd 100644
--- a/server/repositories/pages.js
+++ b/server/repositories/pages.js
@@ -171,10 +171,6 @@ exports.register = function (server, options, next) {
     return db.genericQuery('UPDATE ?? SET hits = hits + 1 WHERE id = ?', [table, pageID]);
   });
 
-  server.expose('update', function (modify, where) {
-    return db.genericQuery('UPDATE ?? SET ? WHERE ?', [table, modify, where]);
-  });
-
   server.expose('updateById', function (modify, pageID) {
     return db.genericQuery('UPDATE ?? SET ? WHERE id = ?', [table, modify, pageID])
       .then(function (result) {
diff --git a/server/services/pages.js b/server/services/pages.js
index a65925b..6651d46 100644
--- a/server/services/pages.js
+++ b/server/services/pages.js
@@ -31,6 +31,7 @@ exports.register = function (server, options, next) {
   });
 
   server.expose('create', function (payload) {
+    let resultPageId;
     return browserscopeRepo.addTest(payload.title, payload.info, payload.slug)
       .then(function (testKey) {
         var page = _omit(payload, 'test');
@@ -42,29 +43,39 @@ exports.register = function (server, options, next) {
         return pagesRepo.create(page);
       })
       .then(function (pageID) {
+        resultPageId = pageID;
         return testsRepo.bulkCreate(pageID, payload.test);
-      });
+      })
+      .then(() => resultPageId);
   });
 
   server.expose('edit', function (payload, isOwn, maxRev, pageId) {
-    let newRev = ++maxRev;
-    return browserscopeRepo.addTest(payload.title, payload.info, payload.slug, newRev)
-      .then(testKey => {
-        let page = _omit(payload, 'test');
-        if (testKey) {
-          page.browserscopeID = testKey;
-        }
-        if (isOwn) {
-          return pagesRepo.updateById(page, pageId);
-        } else {
-          page.published = new Date();
-          page.revision = newRev;
-          return pagesRepo.create(page);
-        }
-      })
-      .then(function (pageID) {
-        return testsRepo.bulkUpdate(pageID, payload.test, isOwn);
-      });
+    const page = _omit(payload, 'test');
+
+    let resultingRevision = maxRev;
+    if (!isOwn) {
+      resultingRevision = maxRev + 1;
+    }
+
+    const slug = resultingRevision > 1 ? `${payload.slug}/${resultingRevision}` : payload.slug;
+    return browserscopeRepo.addTest(payload.title, payload.info, slug)
+    .then(testKey => {
+      if (testKey) {
+        page.browserscopeID = testKey;
+      }
+
+      if (isOwn) {
+        return pagesRepo.updateById(page, pageId);
+      }
+
+      // someone is effectively forking your page so create a new one
+      page.published = new Date();
+      // FIXME: if slug has changed from original page, revision needs to be set to 1 or else can't fetch page by slug root
+      page.revision = resultingRevision;
+      return pagesRepo.create(page);
+    })
+    .then((currentPageID) => testsRepo.bulkUpdate(currentPageID, payload.test, isOwn))
+    .then(() => resultingRevision);
   });
 
   server.expose('getPopular', function () {
@@ -109,13 +120,12 @@ exports.register = function (server, options, next) {
             return resolve();
           }
 
-          const s = page.revision > 1 ? page.slug + '/' + page.revision : page.slug;
-
-          browserscopeRepo.addTest(page.title, page.info, s)
+          const slug = page.revision > 1 ? `${page.slug}/${page.revision}` : page.slug;
+          browserscopeRepo.addTest(page.title, page.info, slug)
             .then(function (testKey) {
               if (testKey) {
                 page.browserscopeID = testKey;
-                pagesRepo.update({ browserscopeID: testKey }, { id: page.id })
+                pagesRepo.updateById({ browserscopeID: testKey }, page.id)
                 .then(resolve)
                 .catch(reject);
               } else {
diff --git a/server/web/auth/github.js b/server/web/auth/github.js
index bf4b4a5..5b15435 100644
--- a/server/web/auth/github.js
+++ b/server/web/auth/github.js
@@ -12,6 +12,18 @@ exports.register = function (server, options, next) {
     }
   });
 
+  // attach credentials to view context to let people know they are logged in
+  // attach authorSlug to view context to construct "My Tests" link in footer
+  server.ext('onPreResponse', function (request, reply) {
+    const response = request.response;
+    if (response.variety === 'view') {
+      response.source.context = response.source.context || {};
+      response.source.context.credentials = request.auth.isAuthenticated ? request.auth.credentials : null;
+      response.source.context.authorSlug = request.yar.get('authorSlug');
+    }
+    return reply.continue();
+  });
+
   return next();
 };
 
diff --git a/server/web/edit/index.hbs b/server/web/edit/index.hbs
index 63af9f0..3fddd0b 100644
--- a/server/web/edit/index.hbs
+++ b/server/web/edit/index.hbs
@@ -1,4 +1,4 @@
-{{#if authorized}}
+{{#if credentials}}
 <h1>Editing <a href="/{{page.slug}}{{#compare 1 page.revision}}/{{page.revision}}{{/compare}}">{{addCode page.title}}</a></h1>
 <p>
 {{#if isOwn}}
@@ -18,6 +18,8 @@ This edit will create a new revision.
           value="{{page.author}}"
         {{else if isOwn}}
           value="{{page.author}}"
+        {{else}}
+          value="{{credentials.displayName}}"
         {{/if}}
         >
     </div>
@@ -29,6 +31,8 @@ This edit will create a new revision.
             value="{{page.authorEmail}}"
           {{else if isOwn}}
             value="{{page.authorEmail}}"
+          {{else}}
+            value="{{credentials.email}}"
           {{/if}}
         > (won’t be displayed; might be used for Gravatar)
       </label>
@@ -40,6 +44,12 @@ This edit will create a new revision.
         value="{{page.authorURL}}"
       {{else if isOwn}}
         value="{{page.authorURL}}"
+      {{else}}
+        {{#if credentials.raw.blog}}
+          value="{{credentials.raw.blog}}"
+        {{else}}
+          value="{{credentials.raw.html_url}}"
+        {{/if}}
       {{/if}}
       >
     </div>
diff --git a/server/web/edit/index.js b/server/web/edit/index.js
index 6440071..f347949 100644
--- a/server/web/edit/index.js
+++ b/server/web/edit/index.js
@@ -37,7 +37,6 @@ exports.register = function (server, options, next) {
             isOwn: isOwn,
             isAdmin: isAdmin,
             page: page,
-            authorized: request.auth.isAuthenticated,
             mediumTextLength: defaults.mediumTextLength
           });
         })
@@ -75,7 +74,6 @@ exports.register = function (server, options, next) {
           },
           jsClass: true,
           page: page,
-          authorized: request.auth.isAuthenticated,
           mediumTextLength: defaults.mediumTextLength
         }).code(400);
       };
@@ -108,23 +106,23 @@ exports.register = function (server, options, next) {
           }
           errResp(errObj);
         } else {
-          let payload = pageWithTests;
-          let isOwn, page;
+          let isOwn = false;
 
-          pagesService.getBySlug(request.params.testSlug, request.params.rev).then(values => {
-            page = values[0];
+          pagesService.getBySlug(request.params.testSlug, request.params.rev)
+          .then(values => {
+            const prevPage = values[0];
             const own = request.yar.get('own') || {};
-            isOwn = own[page.id];
+            isOwn = own[prevPage.id];
             const isAdmin = request.yar.get('admin');
             let update = !!(isAdmin || isOwn);
-            return pagesService.edit(payload, update, page.maxRev, page.id);
-          }).then(updateResult => {
-            request.yar.set('authorSlug', payload.author.replace(' ', '-').replace(/[^a-zA-Z0-9 -]/, ''));
-            if (isOwn) {
-              reply.redirect(`/${page.slug}`);
-            } else {
-              reply.redirect(`/${page.slug}/${(page.maxRev + 1)}`);
-            }
+            return pagesService.edit(pageWithTests, update, prevPage.maxRev, prevPage.id);
+          })
+          .then(resultingRevision => {
+            request.yar.set('authorSlug', pageWithTests.author.replace(' ', '-').replace(/[^a-zA-Z0-9 -]/, ''));
+
+            const r = resultingRevision > 1 ? `/${resultingRevision}` : '';
+
+            reply.redirect(`/${request.params.testSlug}${r}`);
           }).catch(errResp);
         }
       });
diff --git a/server/web/home/index.hbs b/server/web/home/index.hbs
index 8377ddf..f5baa4a 100644
--- a/server/web/home/index.hbs
+++ b/server/web/home/index.hbs
@@ -9,7 +9,7 @@
 <p>jsPerf aims to provide an easy way to create and share <a href="/browse" title="View some examples by browsing the jsPerf test cases">test cases</a>, comparing the performance of different JavaScript snippets by running benchmarks. For more information, see <a href="/faq" title="Frequently asked questions">the FAQ</a>.</p>
 
 <h2>Create a test case</h2>
-{{#if authorized}}
+{{#if credentials}}
 
 {{error genError tag="p"}}
 
@@ -19,13 +19,35 @@
     <h3>Your details (optional)</h3>
 
     <div>
-      <label for="author">Name </label><input type="text" name="author" id="author" value="{{author}}">
+      <label for="author">Name </label><input type="text" name="author" id="author"
+      {{#if author}}
+        value="{{author}}"
+      {{else}}
+        value="{{credentials.displayName}}"
+      {{/if}}
+      >
     </div>
     <div>
-      <label for="authorEmail">Email </label><label class="inline"><input type="email" name="authorEmail" id="authorEmail" value="{{authorEmail}}"> (won’t be displayed; might be used for Gravatar)</label>
+      <label for="authorEmail">Email </label><label class="inline"><input type="email" name="authorEmail" id="authorEmail"
+        {{#if authorEmail}}
+          value="{{authorEmail}}"
+        {{else}}
+          value="{{credentials.email}}"
+        {{/if}}
+      > (won’t be displayed; might be used for Gravatar)</label>
     </div>
     <div>
-      <label for="authorURL">URL </label><input type="url" name="authorURL" id="authorURL" value="{{authorURL}}">
+      <label for="authorURL">URL </label><input type="url" name="authorURL" id="authorURL"
+        {{#if authorURL}}
+          value="{{authorURL}}"
+        {{else}}
+          {{#if credentials.raw.blog}}
+            value="{{credentials.raw.blog}}"
+          {{else}}
+            value="{{credentials.raw.html_url}}"
+          {{/if}}
+        {{/if}}
+      >
     </div>
   </fieldset>
 
diff --git a/server/web/home/index.js b/server/web/home/index.js
index eb1ef4e..9b00e15 100644
--- a/server/web/home/index.js
+++ b/server/web/home/index.js
@@ -23,8 +23,7 @@ exports.register = function (server, options, next) {
         },
         jsClass: true,
         mainJS: true,
-        test: [defaults.test, defaults.test],
-        authorized: request.auth.isAuthenticated
+        test: [defaults.test, defaults.test]
       }));
     }
   });
@@ -46,8 +45,7 @@ exports.register = function (server, options, next) {
             slug: 'browse'
           },
           jsClass: true,
-          mainJS: true,
-          authorized: request.auth.isAuthenticated
+          mainJS: true
         }, errObj)).code(400);
       };
 
@@ -88,13 +86,17 @@ exports.register = function (server, options, next) {
           var payload = pageWithTests;
 
           pagesService.checkIfSlugAvailable(server, payload.slug)
-          .then(function (isAvail) {
+          .then(isAvail => {
             if (!isAvail) {
               errResp({
                 slugError: defaults.errors.slugDupe
               });
             } else {
-              return pagesService.create(payload).then(function () {
+              return pagesService.create(payload)
+              .then(resultPageId => {
+                const own = request.yar.get('own') || {};
+                own[resultPageId] = true;
+                request.yar.set('own', own);
                 request.yar.set('authorSlug', payload.author.replace(' ', '-').replace(/[^a-zA-Z0-9 -]/, ''));
                 reply.redirect('/' + payload.slug);
               });
diff --git a/templates/partials/footer.hbs b/templates/partials/footer.hbs
index 2055fe1..8c484d5 100644
--- a/templates/partials/footer.hbs
+++ b/templates/partials/footer.hbs
@@ -16,14 +16,11 @@
             <a href="/">Add</a>
           </li>
 
-            {{!-- TODO: this is never added to the context, but set on session after test added
-            https://github.com/mtharrison/hapi-context-credentials/blob/master/index.js
-            --}}
-            {{#if authorSlug}}
-              <li>
-                <a href="/browse/{{authorSlug}}">My tests</a> &middot;
-              </li>
-            {{/if}}
+          {{#if authorSlug}}
+            <li>
+              <a href="/browse/{{authorSlug}}">My tests</a>
+            </li>
+          {{/if}}
 
           <li>
             <a href="/browse">Latest</a>
diff --git a/templates/partials/header.hbs b/templates/partials/header.hbs
index 2813cc9..d4be493 100644
--- a/templates/partials/header.hbs
+++ b/templates/partials/header.hbs
@@ -37,5 +37,9 @@
 </head>
 
 <body>
-
   <article>
+  {{#if credentials}}
+    <header>
+      <em class="buttons">Logged in as <a href={{credentials.raw.html_url}}>@{{credentials.username}}</a></em>
+    </header>
+  {{/if}}


Test patch:
diff --git a/public/_js/test.js b/public/_js/test.js
index a9a3ea9..cd12aa8 100644
--- a/public/_js/test.js
+++ b/public/_js/test.js
@@ -21222,17 +21222,6 @@
         setHTML('run', texts.run.ready);
         setStatus(texts.status.ready);
 
-        // prefill author details (only for pages with a comment form)
-        var $author = $('author');
-        if ($author && has.localStorage) {
-          _.each([$author, $('authorEmail'), $('authorURL')], function (element) {
-            element.value = localStorage[element.id] || '';
-            element.oninput = element.onkeydown = function (event) {
-              event && event.type < 'k' && (element.onkeydown = null);
-              localStorage[element.id] = element.value;
-            };
-          });
-        }
         // show warning when Firebug is enabled (avoids showing for Firebug Lite)
         try {
           // Firebug 1.9 no longer has `console.firebug`
diff --git a/server/repositories/tests.js b/server/repositories/tests.js
index bbbf887..856dd85 100644
--- a/server/repositories/tests.js
+++ b/server/repositories/tests.js
@@ -3,10 +3,9 @@ const table = 'tests';
 
 exports.register = function (server, options, next) {
   const db = server.plugins.db;
+  const columns = ['pageID', 'title', 'defer', 'code'];
 
   server.expose('bulkCreate', function (pageID, tests) {
-    var columns = ['pageID', 'title', 'defer', 'code'];
-
     var values = [];
     for (var i = 0, tl = tests.length; i < tl; i++) {
       var test = tests[i];
@@ -23,25 +22,26 @@ exports.register = function (server, options, next) {
       });
   });
 
-  server.expose('bulkUpdate', function (pageID, tests, update) {
-    const columns = ['pageID', 'title', 'defer', 'code'];
+  server.expose('bulkUpdate', function (pageID, tests, isOwn) {
     let queries = [];
 
     tests.forEach(test => {
-      // FIXME
+      // setting existing test title and code to blank indicates it should be deleted ...
       if (!test.title && !test.code) {
-        if (update && test.testID) {
-          queries.push(db.genericQuery(`DELETE FROM tests WHERE pageID = ${pageID} AND testID = ${test.testID}`));
+        // ... if it's theirs to delete ...
+        if (isOwn && test.testID) {
+          queries.push(db.genericQuery(`DELETE FROM ?? WHERE pageID = ${pageID} AND testID = ${test.testID}`, [table]));
         }
+        // ... otherwise skip over the test
       } else {
-        // Update test
         if (test.testID) {
-          queries.push(db.genericQuery(`UPDATE tests SET title = ${db.escape(test.title)}, defer =  ${db.escape(test.defer)} , code =  ${db.escape(test.code)} WHERE pageID = ${pageID} AND testID = ${test.testID}`));
+          queries.push(db.genericQuery(`UPDATE ?? SET title = ${db.escape(test.title)}, defer =  ${db.escape(test.defer)} , code =  ${db.escape(test.code)} WHERE pageID = ${pageID} AND testID = ${test.testID}`, [table]));
         } else {
           queries.push(db.genericQuery(`INSERT INTO ?? (??) VALUES (${pageID}, ${db.escape(test.title)}, ${db.escape(test.defer)}, ${db.escape(test.code)})`, [table, columns]));
         }
       }
     });
+
     return Promise.all(queries).then((results) => {
       server.log(['debug'], `${name}::bulkUpdate results - ${JSON.stringify(results)}`);
       results.forEach((result) => {
diff --git a/server/web/test/index.hbs b/server/web/test/index.hbs
index 13e2552..6410c5f 100644
--- a/server/web/test/index.hbs
+++ b/server/web/test/index.hbs
@@ -166,24 +166,46 @@
     {{/each}}
   </div>
 
-  {{#if authorized}}
+  {{#if credentials}}
     <form action="/{{page.slug}}{{#compare 1 page.revision}}/{{page.revision}}{{/compare}}#comment-form" method="post" id="comment-form">
       <fieldset>
         <h2>Add a comment</h2>
         <div>
           <label for="author">Name <em title="This field is required">*</em> </label>
-          <input type="text" name="author" id="author" {{#if author}}value={{author}}{{/if}} required>
+          <input type="text" name="author" id="author" required
+            {{#if author}}
+              value="{{author}}"
+            {{else}}
+              value="{{credentials.displayName}}"
+            {{/if}}
+          >
           {{#if authorError}} {{authorError}}{{/if}}
         </div>
         <div>
           <label for="authorEmail">Email <em title="This field is required">*</em> </label>
           {{#unless mailError}}<label class="inline">{{/unless}}
-            <input type="email" name="authorEmail" id="authorEmail" {{#if authorEmail}}value={{authorEmail}}{{/if}} required>
+            <input type="email" name="authorEmail" id="authorEmail" required
+              {{#if authorEmail}}
+                value="{{authorEmail}}"
+              {{else}}
+                value="{{credentials.email}}"
+              {{/if}}
+            >
             {{#if authorEmailError}} {{authorEmailError}}{{else}}(only used for Gravatar)</label>{{/if}}
         </div>
         <div>
           <label for="authorURL">URL </label>
-          <input type="url" name="authorURL" id="authorURL" {{#if authorURL}}value={{authorURL}}{{/if}}>
+          <input type="url" name="authorURL" id="authorURL"
+            {{#if authorURL}}
+              value="{{authorURL}}"
+            {{else}}
+              {{#if credentials.raw.blog}}
+                value="{{credentials.raw.blog}}"
+              {{else}}
+                value="{{credentials.raw.html_url}}"
+              {{/if}}
+            {{/if}}
+          >
           {{#if authorURLError}} {{authorURLError}}{{/if}}
         </div>
         <div>
diff --git a/server/web/test/index.js b/server/web/test/index.js
index 523ced2..b14842a 100644
--- a/server/web/test/index.js
+++ b/server/web/test/index.js
@@ -75,8 +75,7 @@ exports.register = function (server, options, next) {
           hasPrep: hasPrep,
           hasSetupOrTeardown: hasSetupOrTeardown,
           stripped: stripped,
-          page: page,
-          authorized: request.auth.isAuthenticated
+          page: page
         };
       })
       .catch((err) => {
diff --git a/test/e2e/home.js b/test/e2e/home.js
index b32291f..44b4598 100644
--- a/test/e2e/home.js
+++ b/test/e2e/home.js
@@ -8,6 +8,7 @@ lab.experiment('Home page', () => {
   let driver;
   let sessionID;
   let passed;
+  let t = 0;
 
   lab.beforeEach(() => {
     passed = false;
@@ -24,7 +25,7 @@ lab.experiment('Home page', () => {
     driver.quit();
 
     Helper.saucelabs.updateJob(sessionID, {
-      name: lab._current.experiments[0].tests[0].title,
+      name: lab._current.experiments[0].tests[t++].title,
       passed
     }, done);
   });
@@ -111,9 +112,6 @@ lab.experiment('Home page', () => {
       const now = Date.now();
 
       return Promise.all([
-        fillOut('author', 'Max Tester'),
-        fillOut('authorEmail', 'test@jsperf.test'),
-        fillOut('authorURL', 'http://jsperf.test'),
         fillOut('title', 'Test ' + now),
         check('visible'),
         fillOut('info', 'This is my test at ' + now),
@@ -158,4 +156,13 @@ lab.experiment('Home page', () => {
       passed = true;
     });
   });
+
+  /*
+    TODO: test additional flows
+
+    - edit page, add new test, save, verify page has 3 tests
+    - edit page, blank test title, blank test code, save, verify page has 2 tests
+    - edit page, blank test title, save, verify did not save and validation message exists on title
+    - edit page, blank test code, save, verify did not save and validation message exists on code
+  */
 });
diff --git a/test/unit/server/repositories/pages.js b/test/unit/server/repositories/pages.js
index 476b415..62f91fe 100644
--- a/test/unit/server/repositories/pages.js
+++ b/test/unit/server/repositories/pages.js
@@ -448,24 +448,6 @@ lab.experiment('Pages Repository', function () {
     });
   });
 
-  lab.experiment('update', function () {
-    lab.test('generic update query', function (done) {
-      var modify = { browserscopeID: 'abc123' };
-      var where = { id: 1 };
-      genericQueryStub.returns(Promise.resolve());
-
-      pages.update(modify, where)
-      .then(function () {
-        Code.expect(genericQueryStub.calledWithExactly(
-          'UPDATE ?? SET ? WHERE ?',
-          [table, modify, where]
-        )).to.be.true();
-
-        done();
-      });
-    });
-  });
-
   lab.experiment('updateById', function () {
     lab.test('generic update query', function (done) {
       var modify = { browserscopeID: 'abc123' };
diff --git a/test/unit/server/repositories/tests.js b/test/unit/server/repositories/tests.js
index a73d802..f603772 100644
--- a/test/unit/server/repositories/tests.js
+++ b/test/unit/server/repositories/tests.js
@@ -174,13 +174,13 @@ lab.experiment('Tests Repository', function () {
       tClone[1].testID = 321;
       tests.bulkUpdate(pageID, tClone, false)
         .then(results => {
-          let call1 = genericQueryStub.getCall(0).args;
-          call1 = Hoek.flatten(call1).join(',');
-          let call2 = genericQueryStub.getCall(1).args;
-          call2 = Hoek.flatten(call2).join(',');
+          const call1 = genericQueryStub.getCall(0).args;
+          const call2 = genericQueryStub.getCall(1).args;
 
-          Code.expect(call2).to.equal('UPDATE tests SET title = `t2`, defer =  `n` , code =  `a = 2` WHERE pageID = 1 AND testID = 321');
-          Code.expect(call1).to.equal('UPDATE tests SET title = `t1`, defer =  `n` , code =  `a = 1` WHERE pageID = 1 AND testID = 123');
+          Code.expect(call2[0]).to.equal('UPDATE ?? SET title = `t2`, defer =  `n` , code =  `a = 2` WHERE pageID = 1 AND testID = 321');
+          Code.expect(call2[1]).to.equal(['tests']);
+          Code.expect(call1[0]).to.equal('UPDATE ?? SET title = `t1`, defer =  `n` , code =  `a = 1` WHERE pageID = 1 AND testID = 123');
+          Code.expect(call1[1]).to.equal(['tests']);
           done();
         });
     });
@@ -198,12 +198,12 @@ lab.experiment('Tests Repository', function () {
       tests.bulkUpdate(pageID, tClone, true)
         .then(results => {
           let call1 = genericQueryStub.getCall(0).args;
-          call1 = Hoek.flatten(call1).join(',');
           let call2 = genericQueryStub.getCall(1).args;
-          call2 = Hoek.flatten(call2).join(',');
 
-          Code.expect(call1).to.equal('DELETE FROM tests WHERE pageID = 1 AND testID = 123');
-          Code.expect(call2).to.equal('DELETE FROM tests WHERE pageID = 1 AND testID = 321');
+          Code.expect(call1[0]).to.equal('DELETE FROM ?? WHERE pageID = 1 AND testID = 123');
+          Code.expect(call1[1]).to.equal(['tests']);
+          Code.expect(call2[0]).to.equal('DELETE FROM ?? WHERE pageID = 1 AND testID = 321');
+          Code.expect(call2[1]).to.equal(['tests']);
 
           done();
         });
diff --git a/test/unit/server/services/pages.js b/test/unit/server/services/pages.js
index a89e0ae..aebd131 100644
--- a/test/unit/server/services/pages.js
+++ b/test/unit/server/services/pages.js
@@ -15,7 +15,6 @@ const MockPagesRepo = {
     server.expose('find', function () {});
     server.expose('updateHits', function () {});
     server.expose('getBySlug', function () {});
-    server.expose('update', function () {});
     server.expose('findBySlug', function () {});
     server.expose('getVisibleBySlug', function () {});
     server.expose('findVisibleBySlug', function () {});
@@ -97,7 +96,6 @@ lab.experiment('Pages Service', function () {
         find: s.stub(server.plugins['repositories/pages'], 'find'),
         updateHits: s.stub(server.plugins['repositories/pages'], 'updateHits'),
         getBySlug: s.stub(server.plugins['repositories/pages'], 'getBySlug'),
-        update: s.stub(server.plugins['repositories/pages'], 'update'),
         findBySlug: s.stub(server.plugins['repositories/pages'], 'findBySlug'),
         getVisibleBySlug: s.stub(server.plugins['repositories/pages'], 'getVisibleBySlug'),
         findVisibleBySlug: s.stub(server.plugins['repositories/pages'], 'findVisibleBySlug'),
@@ -296,8 +294,11 @@ lab.experiment('Pages Service', function () {
       pagesRepoStub.create.returns(Promise.resolve());
       testsRepoStub.bulkCreate.returns(Promise.resolve());
 
-      pages.edit(payload)
-      .then(done)
+      pages.edit(payload, false, 1)
+      .then((resultingRevision) => {
+        Code.expect(resultingRevision).to.equal(2);
+        done();
+      })
       .catch(done);
     });
 
@@ -503,7 +504,7 @@ lab.experiment('Pages Service', function () {
       var testErr = new Error(testErrMsg);
       pagesRepoStub.getBySlug.returns(Promise.resolve([{ id: 1 }]));
       bsRepoStub.addTest.returns(Promise.resolve('abc123'));
-      pagesRepoStub.update.returns(Promise.reject(testErr));
+      pagesRepoStub.updateById.returns(Promise.reject(testErr));
 
       pages.getBySlug(slug, rev)
       .catch(function (err) {
@@ -586,7 +587,7 @@ lab.experiment('Pages Service', function () {
       const newBsKey = 'abc123';
       pagesRepoStub.getBySlug.returns(Promise.resolve([{ id: 1, revision: 2 }]));
       bsRepoStub.addTest.returns(Promise.resolve(newBsKey));
-      pagesRepoStub.update.returns(Promise.resolve());
+      pagesRepoStub.updateById.returns(Promise.resolve());
       testsRepoStub.findByPageID.returns(Promise.resolve([]));
       pagesRepoStub.findBySlug.returns(Promise.resolve([]));
       commentsRepoStub.findByPageID.returns(Promise.resolve([]));
diff --git a/test/unit/server/web/auth/github.js b/test/unit/server/web/auth/github.js
index 945344d..a3cb33f 100644
--- a/test/unit/server/web/auth/github.js
+++ b/test/unit/server/web/auth/github.js
@@ -27,6 +27,11 @@ MockAuthGitHub.register.attributes = {
   name: 'github'
 };
 
+const YarPlugin = {
+  register: require('yar'),
+  options: { cookieOptions: { password: 'password-should-be-32-characters' } }
+};
+
 lab.experiment('auth/GitHub', function () {
   lab.beforeEach(function (done) {
     server = new Hapi.Server();
@@ -36,7 +41,8 @@ lab.experiment('auth/GitHub', function () {
     server.register([
       require('hapi-auth-cookie'),
       MockAuthGitHub,
-      GitHubPlugin
+      GitHubPlugin,
+      YarPlugin
     ], done);
   });
 
@@ -60,4 +66,70 @@ lab.experiment('auth/GitHub', function () {
       });
     });
   });
+
+  lab.experiment('onPreResponse', () => {
+    lab.beforeEach((done) => {
+      server.register(require('vision'), (err) => {
+        Code.expect(err).to.not.exist();
+
+        server.views({
+          engines: {
+            hbs: require('handlebars')
+          },
+          path: __dirname,
+          isCached: false
+        });
+
+        done();
+      });
+    });
+
+    lab.test('does not populate credentials if not authenticated', (done) => {
+      server.route({
+        method: 'GET',
+        path: '/',
+        handler: function (request, reply) {
+          return reply.view('test');
+        }
+      });
+
+      server.inject({ method: 'GET', url: '/' }, function (res) {
+        Code.expect(res.result.trim()).to.equal('Hello');
+        done();
+      });
+    });
+
+    lab.test('populates credentials if authenticated', (done) => {
+      const displayName = 'John';
+      server.route({
+        method: 'GET',
+        path: '/',
+        handler: function (request, reply) {
+          request.auth.isAuthenticated = true;
+          request.auth.credentials = { displayName };
+          return reply.view('test');
+        }
+      });
+
+      server.inject({ method: 'GET', url: '/' }, function (res) {
+        Code.expect(res.result.trim()).to.equal('Hello ' + displayName);
+        done();
+      });
+    });
+
+    lab.test('uses existing context', (done) => {
+      server.route({
+        method: 'GET',
+        path: '/',
+        handler: function (request, reply) {
+          return reply.view('test', { a: 1 });
+        }
+      });
+
+      server.inject({ method: 'GET', url: '/' }, function (res) {
+        Code.expect(res.result.trim()).to.equal('Hello 1');
+        done();
+      });
+    });
+  });
 });
diff --git a/test/unit/server/web/auth/test.hbs b/test/unit/server/web/auth/test.hbs
new file mode 100644
index 0000000..940d58d
--- /dev/null
+++ b/test/unit/server/web/auth/test.hbs
@@ -0,0 +1,1 @@
+Hello {{credentials.displayName}}{{a}}
diff --git a/test/unit/server/web/edit/index.js b/test/unit/server/web/edit/index.js
index 2839fd4..7f77564 100644
--- a/test/unit/server/web/edit/index.js
+++ b/test/unit/server/web/edit/index.js
@@ -77,7 +77,8 @@ lab.beforeEach(function (done) {
       layout: true,
       helpersPath: 'templates/helpers',
       partialsPath: 'templates/partials',
-      relativeTo: path.join(__dirname, '..', '..', '..', '..', '..')
+      relativeTo: path.join(__dirname, '..', '..', '..', '..', '..'),
+      context: (request) => ({ credentials: request.auth.credentials })
     });
     server.register([
       YarPlugin,
@@ -165,7 +166,7 @@ lab.experiment('GET', function () {
     );
 
     server.inject(request, function (response) {
-      Code.expect(response.payload.indexOf('uncheck if you want to fiddle around before making the page public')).to.be.at.least(0);
+      Code.expect(response.payload).to.contain('uncheck if you want to fiddle around before making the page public');
 
       done();
     });
@@ -384,7 +385,7 @@ lab.experiment('POST', function () {
         authorEmail: 'kool-aid@kraft.com',
         authorURL: 'http://kool-aid.com',
         title: 'oh',
-        slug: 'oh-yea',
+        slug: 'wee',
         info: '',
         initHTML: '',
         setup: '',
@@ -507,11 +508,12 @@ lab.experiment('POST', function () {
     });
 
     lab.test('redirects to new url after revision insertion', function (done) {
-      pagesServiceStub.edit.onCall(0).returns(Promise.resolve(request.payload));
+      const rev = 45;
+      pagesServiceStub.edit.onCall(0).returns(Promise.resolve(rev));
 
       server.inject(request, response => {
         Code.expect(response.statusCode).to.equal(302);
-        Code.expect(response.headers.location).to.equal(`/${request.payload.slug}/45`);
+        Code.expect(response.headers.location).to.equal(`/${request.payload.slug}/${rev}`);
 
         done();
       });
@@ -558,8 +560,8 @@ lab.experiment('POST', function () {
     });
 
     lab.test('redirects to original url after update', function (done) {
-      let stubPage = Hoek.applyToDefaults(request.payload, {revision: 1});
-      pagesServiceStub.edit.onCall(0).returns(Promise.resolve(stubPage));
+      const rev = 1;
+      pagesServiceStub.edit.onCall(0).returns(Promise.resolve(rev));
 
       server.inject('/setsession', function (res) {
         var header = res.headers['set-cookie'];
diff --git a/test/unit/server/web/home/index.js b/test/unit/server/web/home/index.js
index 2823f7b..2388150 100644
--- a/test/unit/server/web/home/index.js
+++ b/test/unit/server/web/home/index.js
@@ -54,7 +54,8 @@ lab.beforeEach(function (done) {
       layout: true,
       helpersPath: 'templates/helpers',
       partialsPath: 'templates/partials',
-      relativeTo: path.join(__dirname, '..', '..', '..', '..', '..')
+      relativeTo: path.join(__dirname, '..', '..', '..', '..', '..'),
+      context: (request) => ({ credentials: request.auth.credentials })
     });
     server.register([
       YarPlugin,
@@ -275,6 +276,37 @@ lab.experiment('home', function () {
           done();
         });
       });
+
+      lab.test('sets own in session', (done) => {
+        sinon.stub(server.plugins['services/pages'], 'create').returns(Promise.resolve(true));
+
+        server.route({
+          method: 'GET',
+          path: '/setsession',
+          config: {
+            handler: function (req, reply) {
+              req.yar.set('own', { 2: true });
+              return reply('session set');
+            }
+          }
+        });
+
+        server.inject('/setsession', function (res) {
+          var header = res.headers['set-cookie'];
+          var cookie = header[0].match(/(?:[^\x00-\x20\(\)<>@\,;\:\\'\/\[\]\?\=\{\}\x7F]+)\s*=\s*(?:([^\x00-\x20\'\,\;\\\x7F]*))/); // eslint-disable-line no-control-regex, no-useless-escape
+          request.headers = {};
+          request.headers.cookie = 'session=' + cookie[1];
+
+          server.inject(request, function (response) {
+            Code.expect(response.statusCode).to.equal(302);
+            Code.expect(response.headers.location).to.include(request.payload.slug);
+
+            // could inspect cookie to see `own` was expanded
+
+            done();
+          });
+        });
+      });
     });
   });
 });
diff --git a/test/unit/server/web/test/index.js b/test/unit/server/web/test/index.js
index c5a3cf2..8ddd626 100644
--- a/test/unit/server/web/test/index.js
+++ b/test/unit/server/web/test/index.js
@@ -78,10 +78,11 @@ lab.beforeEach(function (done) {
       layout: true,
       helpersPath: 'templates/helpers',
       partialsPath: 'templates/partials',
-      context: {
+      context: (request) => ({
+        credentials: request.auth.credentials,
         scheme: 'http',
         domain: 'jsperf.test'
-      }
+      })
     });
     server.register([
       YarPlugin,