-
Notifications
You must be signed in to change notification settings - Fork 18
Fixing issues #3, #9, #11 #17
Changes from 14 commits
21856d0
26008e5
0161211
0e8f56c
db1d933
86154cb
c732690
9c96db7
a9de522
0c2314a
b980b0c
cf8ee4f
9d55696
99133ed
5abaca4
0402e4a
1086e79
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,20 @@ describe('Home', () => { | |
cy.visit('/') | ||
}) | ||
}) | ||
describe('PageNotFound', () => { | ||
it('should load error page', () => { | ||
cy.visit('/#/thisisnotavalidpath') | ||
}) | ||
}) | ||
|
||
describe('People', () => { | ||
it('should load', () => { | ||
cy.visit('/#/people/1') | ||
}) | ||
}) | ||
|
||
describe('People', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With BDD style you'd want to have unique |
||
it('should handle bad person request', () => { | ||
cy.visit('/#/people/41234') | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
<template> | ||
<div> | ||
<main> | ||
<notifications group="notifs" /> | ||
<router-view /> | ||
</main> | ||
</div> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,7 @@ | |
<div class="d-flex flex-wrap"> | ||
<router-link | ||
v-for="(person, id) in results" | ||
:to="`/people/${id}`" | ||
:to="`/people/${id + 1}`" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice |
||
:key="id" | ||
class="card m-2" | ||
style="width: 12rem;"> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<template> | ||
<div class="container-fluid"> | ||
<h2>Uh oh, the force was not with that request.</h2> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is great! |
||
<img | ||
:src="'https://upload.wikimedia.org/wikipedia/commons/thumb/1/1e/YODA_%283343464749%29.jpg/640px-YODA_%283343464749%29.jpg'" | ||
alt="Yoda image"> | ||
<div class="row pt-3 pl-3"> | ||
<router-link | ||
:to="`/`"> | ||
<button>Return home</button> | ||
</router-link> | ||
</div> | ||
</div> | ||
</template> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,78 @@ | ||
<template> | ||
<div> | ||
Person {{ $route.params.id }} | ||
<div class="container"> | ||
<!--Only renders template if given results.--> | ||
<div class="card p-3" v-if="results !== undefined || results.length !== 0"> | ||
<h1>{{results.name}}</h1> | ||
<h4>Biography</h4> | ||
<ul class="list-group pb-3" id="bio" > | ||
<li class="list-group-item">Homeworld: <a :href="results.homeworld.url">{{ results.homeworld.name }}</a></li> | ||
<li class="list-group-item">Birth year: {{ results.birth_year }}</li> | ||
<li class="list-group-item">Height: {{ results.height }}</li> | ||
<li class="list-group-item">Mass: {{ results.mass }}</li> | ||
<li class="list-group-item">Species: {{ results.species }}</li> | ||
<li class="list-group-item">Hair color: {{ results.hair_color }}</li> | ||
<li class="list-group-item">Eye color: {{ results.eye_color }}</li> | ||
<li class="list-group-item">Skin color: {{ results.skin_color }}</li> | ||
</ul> | ||
<ul class="list-group pb-3"> | ||
<h4 v-if="results.starships===undefined || results.starships.length !== 0">Starships</h4> <!-- Only show starship if person has starships --> | ||
<li v-for="starship in results.starships" class="list-group-item"> | ||
<a :href="starship.url">{{ starship.name }}</a> | ||
</li> | ||
</ul> | ||
<ul class="list-group"> | ||
<h4 v-if="results.vehicles===undefined || results.vehicles.length !== 0">Vehicles</h4> <!-- Only show vehicles if person has vehicles --> | ||
<li v-for="vehicle in results.vehicles" class="list-group-item"> | ||
<a :href="vehicle.url">{{ vehicle.name }}</a> | ||
</li> | ||
</ul> | ||
<div class="row pt-3 pl-3"> | ||
<router-link | ||
:to="`/`"> | ||
<button>Return home</button> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tip: you can make router-link into a button with the |
||
</router-link> | ||
</div> | ||
</div> | ||
<div class="row" v-else> | ||
<p>uh oh nothing to show here</p> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<script> | ||
import { mapGetters } from 'vuex' | ||
import { mapGetters, mapActions, mapState } from 'vuex' | ||
|
||
import store from '@/store' | ||
|
||
export default { | ||
async beforeRouteEnter (to, from, next) { | ||
await store.dispatch('people/get', to.params.id) | ||
next() | ||
const resp = await store.dispatch('people/getPerson', to.params.id) | ||
// If we receive null back as a response, we redirect back to the home page | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI: A better way to handle this would have been with a try-catch block. |
||
// due to the invalid request | ||
if(resp === null) { | ||
next('/') | ||
} else { | ||
next() | ||
} | ||
}, | ||
async beforeRouteUpdate (to, from, next) { | ||
await store.dispatch('people/get', to.params.id) | ||
next() | ||
const resp = await store.dispatch('people/getPerson', to.params.id) | ||
// Sends a quick notification that the request failed and | ||
// then redirects back to the home page | ||
if(resp === null) { | ||
this.$notify({ | ||
group: 'notifs', | ||
title: 'Error: ' + 'invalid path for a person', | ||
text: 'Taking you back home.', | ||
type: 'warn' | ||
}); | ||
next('/') | ||
} else { | ||
next() | ||
} | ||
}, | ||
computed: { | ||
...mapGetters('people', ['person']) | ||
} | ||
...mapState('people', ['results']) | ||
}, | ||
} | ||
</script> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,18 @@ const initialState = () => ({ | |
results: [] | ||
}) | ||
|
||
// Helper function to fetch links from the API and return name | ||
// of starship/planet/vehicle/etc. | ||
async function renderAttributeName (url) { | ||
const results = await fetch(url) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is confusing because you're mixing 2 different but interchangeable concepts: promises and async/await. |
||
.then(function (response) { | ||
if (response.ok) { | ||
return response.json() | ||
} | ||
}) | ||
return results.name | ||
} | ||
|
||
// Export the module so it can be included in the main store. | ||
export default { | ||
// This module is namespaced so that it is a little more self-contained: | ||
|
@@ -17,17 +29,67 @@ export default { | |
actions: { | ||
async list ({ commit }, page = 1) { | ||
// Fetch People from SWAPI and await the response. | ||
const response = await fetch('https://swapi.co/api/people') | ||
// Parse the JSON string returned in the response into a JavaScript | ||
// object and destructure the results property out of that object. | ||
const { results } = await response.json() | ||
if (response.ok) { | ||
// If the response is OK commit the data to the store. | ||
commit('results', results) | ||
} else { | ||
// If the response is not OK, log the response to the console. | ||
console.error(response) | ||
const results = await fetch('https://swapi.co/api/people') | ||
.then(function (response) { | ||
if (response.ok) { | ||
return response.json() | ||
} | ||
}) | ||
commit('results', results.results) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately, this change is a step back because it's not handling the error case. |
||
}, | ||
async getPerson ({ commit }, id) { | ||
// Fetch People from SWAPI and await the response. | ||
const apiUrl = 'https://swapi.co/api/people/' + id | ||
|
||
const results = await fetch(apiUrl) | ||
.then(function (response) { | ||
if (response.ok) { | ||
return response.json() | ||
} else { | ||
return null | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You generally don't want to be returning values in store actions. The data should flow in a single direction so that the store is the single source of truth for consumers. |
||
} | ||
}) | ||
if (results === null) { | ||
return null | ||
} | ||
const homeworldUrl = results.homeworld | ||
const homeworld = await renderAttributeName(homeworldUrl) | ||
|
||
// saving in this format for easy template rendering | ||
results.homeworld = { 'name': homeworld, 'url': homeworldUrl } | ||
|
||
const species = await renderAttributeName(results.species[0]) | ||
results.species = species | ||
|
||
// Only fetch the vehicle names from the API if the person has vehicles | ||
if (results.vehicles !== undefined || results.vehicles.length !== 0) { | ||
let vehicles = [] | ||
for (var i = 0; i < results.vehicles.length; i++) { | ||
var vehicleUrl = results.vehicles[i] | ||
const vehicleName = await renderAttributeName(vehicleUrl) | ||
vehicles[i] = {'name': vehicleName, 'url': vehicleUrl} | ||
} | ||
results.vehicles = vehicles | ||
} | ||
|
||
// Only fetch the starship names from the API if the person has starships | ||
if (results.starships !== undefined || results.starships.length !== 0) { | ||
let starships = [] | ||
for (var j = 0; j < results.starships.length; j++) { | ||
var starshipUrl = results.starships[j] | ||
const starshipName = await renderAttributeName(starshipUrl) | ||
starships[j] = {'name': starshipName, 'url': starshipUrl} | ||
} | ||
results.starships = starships | ||
} | ||
|
||
commit('results', results) | ||
return results | ||
} | ||
}, | ||
getters: { | ||
getResults: state => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't a useful getter since it's only returning the state which can already be retrieved by other methods. |
||
return state.results | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { mount, createLocalVue } from '@vue/test-utils' | ||
import Vuex from 'vuex' | ||
|
||
import store from '@/store' | ||
import Person from '@/components/Person' | ||
import 'isomorphic-fetch' | ||
import VueRouter from 'vue-router' | ||
|
||
const localVue = createLocalVue() | ||
|
||
localVue.use(Vuex) | ||
localVue.use(VueRouter) | ||
|
||
test('Home renders correctly', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test description should probably say |
||
const wrapper = mount(Person, { store, localVue }) | ||
expect(wrapper.html()).toMatchSnapshot() | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: space above.