Skip to content

Feature: pre-render pages (new PR to trigger preview deployments on netlify) #500

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

Merged
merged 9 commits into from
Jul 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

Note that each ProtoSchool event also maintains its own Code of Conduct, which is
linked from our [event listings](https://proto.school/#/events). Please refer to
linked from our [event listings](https://proto.school/events). Please refer to
the local event organizer's Code of Conduct for additional policies that may apply
to its events, website, and/or GitHub repos.

Expand All @@ -70,7 +70,7 @@ Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

Please note that the organizers of each local ProtoSchool [event](https://proto.school/#/events)
Please note that the organizers of each local ProtoSchool [event](https://proto.school/events)
maintain their own Code of Conduct with additional guidance for local events and
any related websites or repositories. To report instances of abusive, harassing, or
otherwise unacceptable behavior that have occurred within local events or repos,
Expand Down
4 changes: 2 additions & 2 deletions DEVELOPING_TUTORIALS.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ $ npm install
$ npm run serve
```

6. Open a web browser to the following address to preview your work: http://localhost:3000/#/
6. Open a web browser to the following address to preview your work: http://localhost:3000

Vue will update your localhost preview automatically as you make changes.

Expand Down Expand Up @@ -342,7 +342,7 @@ The `title` of your tutorial will be seen in course listings on our tutorials pa

![screenshot](public/title-in-featured-tutorials.png)

The `url` will appear in the URL of your tutorial landing page and lessons. For example, `http://proto.school/#/short-tutorial-title/01`. In most cases this will match your tutorial title, but you may find that you need to make it shorter. Note that this URL will also be used to create the abbreviated title that is shown in the breadcrumb navigation and the small header at the top of each page of your tutorial.
The `url` will appear in the URL of your tutorial landing page and lessons. For example, `http://proto.school/short-tutorial-title/01`. In most cases this will match your tutorial title, but you may find that you need to make it shorter. Note that this URL will also be used to create the abbreviated title that is shown in the breadcrumb navigation and the small header at the top of each page of your tutorial.

![screenshot](public/url-breadcrumb-header.png)

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ $ npm install
$ npm run serve
```

View the site on localhost at: http://localhost:3000/#/
View the site on localhost at: http://localhost:3000

## Managing remote data

Expand Down
3 changes: 2 additions & 1 deletion cypress.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"baseUrl": "http://localhost:3000",
"responseTimeout": 60000,
"defaultCommandTimeout": 60000
"defaultCommandTimeout": 60000,
"video": false
}
2 changes: 1 addition & 1 deletion cypress/fixtures/tutorials.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"resources": [
{
"title": "P2P Data Links with Content Addressing",
"link": "https://proto.school/#/basics/",
"link": "https://proto.school/basics/",
"type": "tutorial",
"description": "You've seen the IPFS Files API. Now explore the IPFS DAG API, where you'll use CIDs to create verifiable links between datasets."
}
Expand Down
33 changes: 33 additions & 0 deletions cypress/integration/redirects.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
describe('REDIRECTS', () => {
function assertHashRouteRedirect (path, redirect) {
cy.visit(`/#${path}`)
cy.location('pathname').should('eq', redirect || path)
}

it('404 redirect', () => {
cy.visit('/unknown-path', { failOnStatusCode: false })
cy.location('pathname').should('eq', '/404')
cy.visit('/unknown-path/02', { failOnStatusCode: false })
cy.location('pathname').should('eq', '/404')
})

it('should redirect the old hash urls to the new paths', () => {
assertHashRouteRedirect('/tutorials')
assertHashRouteRedirect('/news')
assertHashRouteRedirect('/host')
assertHashRouteRedirect('/events')
assertHashRouteRedirect('/chapters', '/events')
assertHashRouteRedirect('/build')
assertHashRouteRedirect('/contribute')
assertHashRouteRedirect('/data-structures')
assertHashRouteRedirect('/data-structures/resources')
assertHashRouteRedirect('/data-structures/01')
assertHashRouteRedirect('/basics')
assertHashRouteRedirect('/basics/')
assertHashRouteRedirect('/basics/resources')
assertHashRouteRedirect('/basics/02')
assertHashRouteRedirect('/unknown-route', '/404')
assertHashRouteRedirect('/unknown-route/01', '/404')
assertHashRouteRedirect('/news/01', '/404')
})
})
2 changes: 1 addition & 1 deletion cypress/integration/tutorial-messages.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function visitWithDates ({
passedAt,
lessons = []
}) {
cy.visit(`/#/${url}`, {
cy.visit(`/${url}`, {
onBeforeLoad (window) {
window.__DATA__ = {
tutorials: {
Expand Down
68 changes: 34 additions & 34 deletions cypress/integration/tutorials.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ describe(`DISPLAYS CORRECT TUTORIALS ON HOMEPAGE AND TUTORIALS PAGE`, function (
cy.get('[data-cy=tutorial-card-title]').should('have.length', courses.all.length) // displaying # of tutorials in all array in courses.json
.and('have.length', Object.keys(tutorials).length) // displaying # of tutorials in tutorials.json
for (let i = 0; i < courses.all.length; i++) {
cy.get('[data-cy=tutorial-card-title]').eq(i).should('contain', tutorials[courses.all[i]].title)
const tutorial = tutorials[courses.all[i]]

cy.get('[data-cy=tutorial-card-title]').eq(i).should('contain', tutorial.title)
cy.get(`.tutorial a[href="/${tutorial.url}"]`).click()
cy.location('pathname').should('eq', `/${tutorial.url}`)
cy.visit(`/tutorials`)
}
}

Expand All @@ -47,15 +52,20 @@ describe(`DISPLAYS CORRECT TUTORIALS ON HOMEPAGE AND TUTORIALS PAGE`, function (
}

it(`homepage shows featured tutorials in correct order`, function () {
cy.visit(`/#/`)
cy.visit(`/`)
cy.get('[data-cy=tutorial-card-title]').should('have.length', courses.featured.length)
for (let i = 0; i < courses.featured.length; i++) {
cy.get('[data-cy=tutorial-card-title]').eq(i).should('contain', tutorials[courses.featured[i]].title)
const tutorial = tutorials[courses.featured[i]]

cy.get('[data-cy=tutorial-card-title]').eq(i).should('contain', tutorial.title)
cy.get(`.tutorial a[href="/${tutorial.url}"]`).click()
cy.location('pathname').should('eq', `/${tutorial.url}`)
cy.visit(`/`)
}
})

it(`tutorials page shows all tutorials in correct order`, function () {
cy.visit(`/#/tutorials/`)
cy.visit(`/tutorials`)
assertTutorialsAreNotFiltered()
})

Expand All @@ -76,24 +86,19 @@ describe(`DISPLAYS CORRECT TUTORIALS ON HOMEPAGE AND TUTORIALS PAGE`, function (
})

it(`toggle hides coding tutorials via click and url`, function () {
cy.visit(`/#/tutorials/`)
cy.visit(`/tutorials`)
assertTutorialsAreNotFiltered()
cy.get('[data-cy=toggle-coding-tutorials]').click()
assertTutorialsAreFiltered('all', false)
cy.reload() // reload is necessary because vue router does not listen to changes to the location hash
assertTutorialsAreFiltered('all', false)
cy.visit(`/#/tutorials?code=true`)
cy.reload()
cy.visit(`/tutorials?code=true`)
assertTutorialsAreNotFiltered()
cy.visit(`/#/tutorials?code=false`)
cy.reload()
cy.visit(`/#/tutorials?course=ipfs&code=false`)
cy.reload()
cy.visit(`/tutorials?code=false`)
cy.visit(`/tutorials?course=ipfs&code=false`)
assertTutorialsAreFiltered('ipfs', false)
cy.reload()
assertTutorialsAreFiltered('ipfs', false)
cy.get('[data-cy=toggle-coding-tutorials]').click()
cy.url().should('contain', '/#/tutorials?course=ipfs&code=true')
cy.url().should('contain', '/tutorials?course=ipfs&code=true')
assertTutorialsAreFiltered('ipfs', true)
})

Expand Down Expand Up @@ -148,7 +153,7 @@ function testResetCode (tutorialId, lessonNr) {
const tutorialName = tutorials[tutorialId].url
const tutorialType = getTutorialType(tutorialId)
it(`toggles resetCode in ${tutorialType} challenge (tutorial ${tutorialId} lesson ${lessonNr})`, function () {
cy.visit(`/#/${tutorialName}/${lessonNr}`)
cy.visit(`/${tutorialName}/${lessonNr}`)
cy.get('[data-cy=code-editor-ready]').should('be.visible') // wait for editor to be updated
cy.get('[data-cy=reset-code]').should('not.exist')
cy.get(`[data-cy=progress-not-yet-started]`).should('be.visible')
Expand All @@ -167,7 +172,7 @@ function testViewSolution (tutorialId, lessonNr) {
const tutorialName = tutorials[tutorialId].url
const tutorialType = getTutorialType(tutorialId)
it(`toggles view solution in ${tutorialType} challenge (tutorial ${tutorialId} lesson ${lessonNr})`, function () {
cy.visit(`/#/${tutorialName}/${lessonNr}`)
cy.visit(`/${tutorialName}/${lessonNr}`)
cy.get('[data-cy=code-editor-ready]').should('be.visible') // wait for editor to be updated
cy.get('[data-cy=hide-solution]').should('not.exist')
cy.get('[data-cy=view-solution]').should('be.visible')
Expand All @@ -188,7 +193,7 @@ function testMultipleChoiceOptions (tutorialId, lessonNr) {
const choices = lesson.logic.choices
const correctChoiceIndex = choices.findIndex(choice => choice.correct === true)
it(`displays right number of choices lesson ${lessonNr} and displays as not yet started`, function () {
cy.visit(`/#/${tutorialName}/${lessonNr}`)
cy.visit(`/${tutorialName}/${lessonNr}`)
cy.get('[data-cy=choice]').should('have.length', choices.length)
cy.get('[data-cy=output-success]').should('not.exist')
cy.get('[data-cy=output-fail]').should('not.exist')
Expand Down Expand Up @@ -247,7 +252,7 @@ function advanceThroughLessons (tutorialId) {
lessons.push(resourcesLesson) // index of resourcesLesson = lessonCount

it(`finds ${tutorialTitle} landing page with links to correct ${lessonCount} lessons plus resources`, function () {
cy.visit(`/#/${tutorialName}/`)
cy.visit(`/${tutorialName}/`)
cy.contains('h2', tutorialTitle)
cy.get(`[data-cy=lesson-link-standard]`).should('have.length', lessonCount)
cy.get(`[data-cy=lesson-link-resources]`).should('have.length', 1)
Expand All @@ -256,22 +261,22 @@ function advanceThroughLessons (tutorialId) {
for (let i = 0; i < lessonCount; i++) {
cy.get('[data-cy=lesson-link-standard]').eq(i)
.should('contain', lessons[i].title)
.and('have.attr', 'href', `#/${tutorialName}/${lessons[i].formattedId}`)
.and('have.attr', 'href', `/${tutorialName}/${lessons[i].formattedId}`)
}

cy.get(`[data-cy=lesson-link-resources]`).eq(0)
.should('contain', 'More to explore')
.and('have.attr', 'href', `#/${tutorialName}/resources`)
.and('have.attr', 'href', `/${tutorialName}/resources`)
})

// const hasResources = tutorials[tutorialId].hasOwnProperty('resources')
it(`uses lesson links and nav links btw landing page and 1st lesson`, function () {
cy.visit(`/#/${tutorialName}/`)
cy.get(`[href="#/${tutorialName}/01"]`).click()
cy.url().should('include', `#/${tutorialName}/01`)
cy.visit(`/${tutorialName}/`)
cy.get(`[href="/${tutorialName}/01"]`).click()
cy.url().should('include', `/${tutorialName}/01`)
cy.get(`[data-cy=tutorial-landing-link]`).click() // test nav link back to tutorial landing page
cy.contains('h2', tutorialTitle)
cy.get(`[href="#/${tutorialName}/01"]`).click()
cy.get(`[href="/${tutorialName}/01"]`).click()
cy.contains('h1', lessons[0].title)
})

Expand All @@ -288,7 +293,7 @@ function advanceThroughLessons (tutorialId) {
cy.contains('h1', 'Resources') // loads resources page
cy.get('[data-cy=resources-content]') // loads meaningful content
cy.get('[data-cy=more-tutorials]').click()
cy.url().should('include', `#/tutorials`)
cy.url().should('include', `/tutorials`)
})
return
}
Expand Down Expand Up @@ -445,7 +450,7 @@ function advanceThroughLessons (tutorialId) {
}

function advanceToNextLesson () {
// ADVANCE TO NEXT LESSON AS ABLE OR BY CHEATING, DEPENDING ON LESSON TYPE
// ADVANCE TO NEXT LESSON DEPENDING ON ITS TYPE
let advance = {}

switch (lessonType) {
Expand All @@ -472,14 +477,9 @@ function advanceThroughLessons (tutorialId) {
}

it(`${advance.msg}`, function () {
if (advance.method === 'cheat') {
cy.log(`cannot fully test tutorial ${tutorialId}, lesson ${lessonNr} because it is of type ${lessonType}`)
cy.visit(`/#/${tutorialName}/${nextLessonNr}`)
} else if (advance.method === 'click') {
cy.get(`[data-cy=${advance.buttonData}]`).should('be.visible').and('not.be.disabled')
cy.get(`[data-cy=${advance.buttonData}]`).click()
}
cy.url().should('include', `#/${tutorialName}/${nextLessonNr}`)
cy.get(`[data-cy=${advance.buttonData}]`).should('be.visible').and('not.be.disabled')
cy.get(`[data-cy=${advance.buttonData}]`).click()
cy.url().should('include', `/${tutorialName}/${nextLessonNr}`)
cy.contains('h1', lessons[index + 1].title)
})
}
Expand Down
42 changes: 21 additions & 21 deletions jest/helpers/fixtures.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
const api = require('../../src/api')

// Creators: create data (tutorials, lessons)
async function createTutorial (config = { override: {}, lessons: 0, resources: 0 }) {
const { tutorial, lessons, resources } = await generateTutorial(config)
const createdTutorial = await api.tutorials.create(tutorial)
function createTutorial (config = { override: {}, lessons: 0, resources: 0 }) {
const { tutorial, lessons, resources } = generateTutorial(config)
const createdTutorial = api.tutorials.create(tutorial)

for (let i = 0; i < lessons.length; ++i) {
await api.lessons.create(await api.tutorials.get(createdTutorial.id), lessons[i])
api.lessons.create(api.tutorials.get(createdTutorial.id), lessons[i])
}

resources.forEach(async resource => {
await api.resources.add(createdTutorial.id, resource)
resources.forEach(resource => {
api.resources.add(createdTutorial.id, resource)
})

return api.tutorials.get(createdTutorial.id)
}

// Generators: generate data to be used

async function generateTutorial (config = { override: {}, lessons: 0, resources: 0 }) {
const suffix = await api.tutorials.getNextTutorialId()
function generateTutorial (config = { override: {}, lessons: 0, resources: 0 }) {
const suffix = api.tutorials.getNextTutorialId()

const tutorial = {
title: `New Tutorial (${suffix})`,
Expand All @@ -29,18 +29,18 @@ async function generateTutorial (config = { override: {}, lessons: 0, resources:
...config.override
}

const lessons = await Promise.all(new Array(config.lessons).fill().map(async (_, i) => (
(await generateLesson({ override: { title: `Lesson ${i + 1}` } })).lesson
)))
const lessons = new Array(config.lessons).fill().map((_, i) => (
generateLesson({ override: { title: `Lesson ${i + 1}` } }).lesson
))

const resources = await Promise.all(new Array(config.resources).fill().map(async (_, i) => (
(await generateResource({
const resources = new Array(config.resources).fill().map((_, i) => (
generateResource({
override: {
title: `Resource ${i + 1}`,
link: `https://resource${i + 1}.com`
}
})).resource
)))
}).resource
))

return {
tutorial,
Expand All @@ -50,7 +50,7 @@ async function generateTutorial (config = { override: {}, lessons: 0, resources:
}
}

async function generateLesson ({ createTutorial = false, override = {} } = {}) {
function generateLesson ({ createTutorial = false, override = {} } = {}) {
const lesson = {
title: 'Lesson',
type: 'text',
Expand All @@ -59,8 +59,8 @@ async function generateLesson ({ createTutorial = false, override = {} } = {}) {
let tutorial

if (createTutorial) {
tutorial = await api.tutorials.create((await generateTutorial()).tutorial)
await api.courses.add(tutorial.id)
tutorial = api.tutorials.create(generateTutorial().tutorial)
api.courses.add(tutorial.id)
}

return {
Expand All @@ -70,7 +70,7 @@ async function generateLesson ({ createTutorial = false, override = {} } = {}) {
}
}

async function generateResource ({ createTutorial, override = {} } = {}) {
function generateResource ({ createTutorial, override = {} } = {}) {
const resource = {
title: 'Resource',
link: 'https://resource.com',
Expand All @@ -81,8 +81,8 @@ async function generateResource ({ createTutorial, override = {} } = {}) {
let tutorial

if (createTutorial) {
tutorial = await api.tutorials.create((await generateTutorial()).tutorial)
await api.courses.add(tutorial.id)
tutorial = api.tutorials.create(generateTutorial().tutorial)
api.courses.add(tutorial.id)
}

return {
Expand Down
8 changes: 4 additions & 4 deletions jest/helpers/setup.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const api = require('../../src/api')

async function restoreData (lastTutorialId) {
const newLastTutorialId = (await api.tutorials.list.getLatest()).id
function restoreData (lastTutorialId) {
const newLastTutorialId = api.tutorials.list.getLatest().id

// delete all new tutorials
for (let id = lastTutorialId + 1; id <= newLastTutorialId; id++) {
await api.tutorials.remove(id)
await api.courses.remove(id)
api.tutorials.remove(id)
api.courses.remove(id)
}
}

Expand Down
Loading