Skip to content

Commit

Permalink
✅ Add missing redirects and Cypress tests for them
Browse files Browse the repository at this point in the history
  • Loading branch information
homostellaris committed Mar 8, 2024
1 parent 1df8b47 commit 28763ce
Show file tree
Hide file tree
Showing 15 changed files with 185 additions and 30 deletions.
12 changes: 12 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,15 @@ jobs:
with:
build: npm run build
start: npm run preview
- uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-screenshots
path: cypress/screenshots
if-no-files-found: ignore
- uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-videos
path: cypress/videos
if-no-files-found: ignore
2 changes: 2 additions & 0 deletions cypress.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ export default defineConfig({
return plugins(on, config)
},
baseUrl: 'http://localhost:6602',
video: true,
screenshotOnRunFailure: true,
},
})
79 changes: 72 additions & 7 deletions cypress/e2e/index.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,17 @@ describe('when an organiser wants to schedule a social', () => {

cy.location('pathname').then(pathname => {
const socialId = pathname.split('/')[1]
cy.task('updateSocial', {id: socialId, data: '2024-01-02'})
cy.task('updateSocial', {
id: socialId,
data: {
invitees: {
fakeId: {
name: 'Max',
dates: ['2024-01-02'],
},
},
},
})
})
cy.reload() // This shouldn't be necessary but HTTP2 isn't supported by Cypress so streaming doesn't work
cy.get('.invitee').should('contain.text', 'Max')
Expand Down Expand Up @@ -55,7 +65,7 @@ describe('when an invitee has chosen their availability', () => {
cy.visit(`/${this.social.id}/everyone`)
cy.contains('BACK').click()

// Force a wait for the page to re-render, think this may be caused by the SvelteKit server does intiial render in current time.
// Force a wait for the page to re-render and display date properly, not sure why this re-render happens.
cy.contains('January 1970').should('exist')
cy.contains('.calendar-date', /^3$/).click()
cy.contains('.calendar-date', /^4$/).click()
Expand All @@ -65,9 +75,9 @@ describe('when an invitee has chosen their availability', () => {
.table()
.should('deep.equal', [
['', 'Rank', 'Date', 'Available Invitees', 'Invitees Total'],
['', '1', 'Saturday, 3 January', '🤴🧙‍♂️', '2/2'],
['', '2', 'Sunday, 4 January', '🤴🧙‍♂️', '2/2'],
['', '3', 'Friday, 2 January', '🤴', '1/2'],
['', '1', formattedDate('1970-01-03'), '🤴🧙‍♂️', '2/2'],
['', '2', formattedDate('1970-01-04'), '🤴🧙‍♂️', '2/2'],
['', '3', formattedDate('1970-01-02'), '🤴', '1/2'],
])
})
})
Expand All @@ -91,9 +101,55 @@ describe('when a decision has been made', () => {
cy.setCookie('userId', 'inviteeID')
})

it.skip('anyone can go back and amend it', () => {})
it('anyone can go back and amend it', function () {
cy.visit(`/${this.social.id}/decision`)
cy.contains(`Your social is on ${formattedDate('1970-01-03')}`)
cy.contains('BACK').click()
cy.contains('#best-dates', formattedDate('1970-01-02')).click()
cy.contains('button', 'NEXT').click()
cy.contains(`Your social is on ${formattedDate('1970-01-02')}`)
})

it("redirects invitees when they haven't seen the latest decision", function () {
cy.visit(`/${this.social.id}/everyone`)
cy.url('pathname').should('include', '/decision')
cy.waitUntil(() => cy.getCookie('decision'))

cy.clearCookie('decision')
cy.visit(`/${this.social.id}/you`)
cy.url('pathname').should('include', '/decision')
cy.waitUntil(() => cy.getCookie('decision'))
cy.clearCookie('decision')
cy.visit(`/${this.social.id}/invite`)
cy.url('pathname').should('include', '/decision')
cy.waitUntil(() => cy.getCookie('decision'))
cy.clearCookie('decision')
cy.visit(`/${this.social.id}`)
cy.url('pathname').should('include', '/decision')
cy.waitUntil(() => cy.getCookie('decision'))

it.skip("redirects invitees who haven't seen it yet", () => {})
cy.visit(`/${this.social.id}/everyone`)
cy.url('pathname').should('not.include', '/decision')
cy.visit(`/${this.social.id}/you`)
cy.url('pathname').should('not.include', '/decision')
cy.visit(`/${this.social.id}/invite`)
cy.url('pathname').should('not.include', '/decision')
cy.visit(`/${this.social.id}`)
cy.url('pathname').should('not.include', '/decision')

cy.task('updateSocial', {
id: this.social.id,
data: {decision: '1970-01-02'},
})
cy.visit(`/${this.social.id}/everyone`)
cy.url('pathname').should('include', '/decision')
cy.visit(`/${this.social.id}/you`)
cy.url('pathname').should('include', '/decision')
cy.visit(`/${this.social.id}/invite`)
cy.url('pathname').should('include', '/decision')
cy.visit(`/${this.social.id}`)
cy.url('pathname').should('include', '/decision')
})
})

describe('when an invitee joins the social', () => {
Expand All @@ -105,5 +161,14 @@ describe('when an invitee joins the social', () => {
})

describe('when an invitee from another timezone joins the social', () => {
// Not sure how to test this yet. Manually tested by changing OS timezone.
it.skip('shows them the same dates as others disregarding timezones entirely', () => {})
})

function formattedDate(date) {
return new Date(date).toLocaleDateString(undefined, {
weekday: 'long',
month: 'long',
day: 'numeric',
})
}
37 changes: 19 additions & 18 deletions cypress/plugins/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,7 @@ import {toSocialId, toDatabaseId} from '../../src/lib/id.js'
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
export default (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config

export default (on, _config) => {
on('task', {
async createSocial(data) {
const faunaData = {
Expand All @@ -51,17 +48,14 @@ export default (on, config) => {
},
async updateSocial({id, data}) {
const reference = toDatabaseId(id)
const faunaData = {
data: {
...faunaify(data),
},
}

const response = await client.query(
q.Update(q.Ref(q.Collection('social'), reference), {
data: {
invitees: {
fakeId: {
name: 'Max',
dates: [q.Date(data)],
},
},
},
}),
q.Update(q.Ref(q.Collection('social'), reference), faunaData),
)
return response
},
Expand All @@ -83,16 +77,23 @@ const defaultSocial = {
}

function faunaify(data) {
return {
invitees: Object.fromEntries(
let faunaData = {}
if (data.invitees) {
faunaData.invitees = Object.fromEntries(
Object.entries(data.invitees).map(([id, invitee]) => [
id,
{
name: invitee.name,
dates: invitee.dates.map(date => q.Date(date)),
},
]),
),
organizer: data.organizer,
)
}
if (data.organizer) {
faunaData.organizer = data.organizer
}
if (data.decision) {
faunaData.decision = q.Date(data.decision)
}
return faunaData
}
1 change: 1 addition & 0 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })

import 'cypress-map/commands/table'
import 'cypress-wait-until'
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"ava": "^4.0.0",
"cypress": "^13.6.4",
"cypress-map": "^1.30.0",
"cypress-wait-until": "^3.0.1",
"dotenv": "^16.4.2",
"fauna-shell": "^0.13.0",
"gitmoji-cli": "^9.1.0",
Expand Down
16 changes: 16 additions & 0 deletions src/lib/redirectToDecision.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import cookie from 'cookie'

export function hasDecisionBeenSeen(request, social) {
const cookieHeader = request.headers.get('cookie')
const cookies = cookie.parse(cookieHeader || '')
return social.decision !== cookies.decision
}

export function decisionRedirect(socialId) {
return {
status: 303,
headers: {
location: `/${socialId}/decision`,
},
}
}
3 changes: 2 additions & 1 deletion src/routes/[socialId=socialId]/decision.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script context="module">
import Retreat from '$lib/Back.svelte'
import cookie from 'cookie'
</script>

<script>
Expand All @@ -17,7 +18,7 @@
)
onMount(() => {
sessionStorage.setItem('decisionSeen', social.decision)
document.cookie = cookie.serialize('decision', social.decision)
})
</script>

Expand Down
5 changes: 4 additions & 1 deletion src/routes/[socialId=socialId]/everyone.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import faunadb from 'faunadb'
import {toDatabaseId} from '$lib/id'
import notification from '$lib/server/notification'
import convertDatesToStrings from '$lib/convertDatesToStrings'
import {decisionRedirect, hasDecisionBeenSeen} from '$lib/redirectToDecision'

const q = faunadb.query

Expand All @@ -12,7 +13,7 @@ const client = new faunadb.Client({
secret: process.env.FAUNADB_SERVER_SECRET,
})

export async function get({params, locals}) {
export async function get({params, locals, request}) {
const socialId = params.socialId
const reference = toDatabaseId(socialId)
const response = await client.query(
Expand All @@ -22,6 +23,8 @@ export async function get({params, locals}) {
if (social.decision) social.decision = social.decision.value
const user = social.invitees[locals.userId]

if (hasDecisionBeenSeen(request, social)) return decisionRedirect(socialId)

return {
status: 200,
body: {
Expand Down
3 changes: 2 additions & 1 deletion src/routes/[socialId=socialId]/everyone.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import StreamingStatus from '$lib/StreamingStatus.svelte'
import Inviter from '$lib/Inviter.svelte'
import push from '$lib/push'
import cookie from 'cookie'
const {getAnalytics} = getContext('analytics')
Expand Down Expand Up @@ -59,7 +60,7 @@
const newDecision =
social.decision &&
social.decision.value !== sessionStorage.getItem('decisionSeen')
social.decision.value !== cookie.parse(document.cookie).decision
if (newDecision) goto('decision')
status = '📡 Updated: someone joined the party!'
Expand Down
34 changes: 34 additions & 0 deletions src/routes/[socialId=socialId]/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import faunadb from 'faunadb'
import {toDatabaseId} from '$lib/id'
import convertDatesToStrings from '$lib/convertDatesToStrings'
import {decisionRedirect, hasDecisionBeenSeen} from '$lib/redirectToDecision'

const q = faunadb.query

const client = new faunadb.Client({
domain: process.env.FAUNADB_DOMAIN,
port: process.env.FAUNADB_PORT,
scheme: process.env.FAUNADB_SCHEME,
secret: process.env.FAUNADB_SERVER_SECRET,
})

export async function get({params, locals, request}) {
const socialId = params.socialId
const reference = toDatabaseId(socialId)
const response = await client.query(
q.Get(q.Ref(q.Collection('social'), reference)),
)
const social = convertDatesToStrings(response.data)
if (social.decision) social.decision = social.decision.value
const user = social.invitees[locals.userId]

if (hasDecisionBeenSeen(request, social)) return decisionRedirect(socialId)

return {
status: 200,
body: {
social,
user,
},
}
}
1 change: 1 addition & 0 deletions src/routes/[socialId=socialId]/invite.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export async function get({params, locals}) {
q.Get(q.Ref(q.Collection('social'), reference)),
)
const social = convertDatesToStrings(response.data)
if (social.decision) social.decision = social.decision.value
const user = social.invitees[locals.userId]

if (user) {
Expand Down
6 changes: 5 additions & 1 deletion src/routes/[socialId=socialId]/you.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import faunadb from 'faunadb'
import {toDatabaseId} from '$lib/id'
import convertDatesToStrings from '$lib/convertDatesToStrings'
import {decisionRedirect, hasDecisionBeenSeen} from '$lib/redirectToDecision'

const q = faunadb.query

Expand All @@ -11,15 +12,18 @@ const client = new faunadb.Client({
secret: process.env.FAUNADB_SERVER_SECRET,
})

export async function get({params, locals}) {
export async function get({params, locals, request}) {
const socialId = params.socialId
const reference = toDatabaseId(socialId)
const response = await client.query(
q.Get(q.Ref(q.Collection('social'), reference)),
)
const social = convertDatesToStrings(response.data)
if (social.decision) social.decision = social.decision.value
const user = social.invitees[locals.userId]

if (hasDecisionBeenSeen(request, social)) return decisionRedirect(socialId)

return {
status: 200,
body: {
Expand Down
Loading

0 comments on commit 28763ce

Please sign in to comment.