-
Notifications
You must be signed in to change notification settings - Fork 1
Exercise 7 #13
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
base: main
Are you sure you want to change the base?
Exercise 7 #13
Changes from 12 commits
db8ca28
91a4e36
6490100
b65c69e
700d19d
bb2ac3b
1bc5a13
544b72c
8c6134e
0fe127a
1285621
271baf2
05cf094
bb045a1
e569b39
6f92197
a4ad086
3d31d5a
d680559
c21b280
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 |
|---|---|---|
|
|
@@ -19,4 +19,5 @@ npm-debug.log* | |
| storybook-static | ||
|
|
||
| ### Nuxt.js### | ||
| .nuxt | ||
| .nuxt | ||
| static/sw.js | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,59 @@ | ||||||
| <template> | ||||||
| <div> | ||||||
| <div v-if="loggedIn">You are Logged in</div> | ||||||
| <form onsubmit="event.preventDefault();"> | ||||||
| <input | ||||||
| type="email" | ||||||
| aria-label="Email" | ||||||
| v-model="email" | ||||||
| placeholder="Email" | ||||||
| /> | ||||||
| <input | ||||||
| type="password" | ||||||
| aria-label="Password" | ||||||
| v-model="password" | ||||||
| placeholder="Password" | ||||||
| /> | ||||||
| <div v-if="invalidCredentials">Falsche Email oder Passwort</div> | ||||||
| <input type="submit" aria-label="Login" value="Login" @click="submit" /> | ||||||
| </form> | ||||||
| </div> | ||||||
| </template> | ||||||
|
|
||||||
| <script> | ||||||
| import { mapState, mapGetters, mapActions } from "vuex"; | ||||||
| export default { | ||||||
| computed: { | ||||||
| ...mapGetters(["loggedIn"]), | ||||||
| ...mapState(["currentUser"]), | ||||||
| }, | ||||||
| methods: { | ||||||
| ...mapActions(["login"]), | ||||||
| submit: async function () { | ||||||
|
Contributor
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.
Suggested change
|
||||||
| try { | ||||||
| await this.login({ | ||||||
| email: this.email, | ||||||
| password: this.password, | ||||||
| apolloClient: this.$apollo, | ||||||
| }); | ||||||
| this.$router.push({ | ||||||
| path: "/", | ||||||
| }); | ||||||
| this.invalidCredentials = false; | ||||||
| } catch (error) { | ||||||
| this.invalidCredentials = true; | ||||||
| } | ||||||
| }, | ||||||
| }, | ||||||
| data: function () { | ||||||
|
Contributor
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.
Suggested change
|
||||||
| return { | ||||||
| email: "", | ||||||
| password: "", | ||||||
| invalidCredentials: false, | ||||||
| }; | ||||||
| }, | ||||||
| }; | ||||||
| </script> | ||||||
|
|
||||||
| <style> | ||||||
| </style> | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,40 +2,48 @@ | |
| <div> | ||
| <div class="item-title">{{ item.title }} ({{ item.votes }})</div> | ||
| <div class="item-buttons"> | ||
| <button @click="upvote">Upvote</button> | ||
| <button @click="downvote">Downvote</button> | ||
| <button @click="remove">Remove</button> | ||
| <button v-if="loggedIn" @click="upvote">Upvote</button> | ||
|
Contributor
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.
|
||
| <button v-if="loggedIn" @click="downvote">Downvote</button> | ||
| <button v-if="isAuthor" @click="remove">Remove</button> | ||
| <button v-if="isAuthor" @click="edit">Edit</button> | ||
|
Comment on lines
+5
to
+8
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. ⭐Implemented Upvote and Downvote in logged in state, Remove and Edit only if it's the author. Optional: Only show the upvote or downvote button if you didn't upvoted / downvoted before.
Contributor
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. ➕ 1️⃣ |
||
| </div> | ||
| </div> | ||
| </template> | ||
|
|
||
| <script> | ||
| import { mapGetters, mapState } from "vuex"; | ||
| export default { | ||
| props: ["item"], | ||
| methods: { | ||
| upvote: function () { | ||
| let item = {...this.item}; | ||
| item.votes++; | ||
| this.$emit("updateItem", item); | ||
| this.$emit("upvote"); | ||
|
Contributor
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. I suggest to use |
||
| }, | ||
| downvote: function () { | ||
| let item = {...this.item}; | ||
| item.votes--; | ||
| this.$emit("updateItem", item); | ||
| this.$emit("downvote"); | ||
| }, | ||
| remove: function () { | ||
| this.$emit("removeItem", this.item); | ||
| this.$emit("remove", this.item); | ||
| }, | ||
| edit: function () { | ||
| this.$emit("edit", this.item); | ||
| }, | ||
| }, | ||
| computed: { | ||
| ...mapGetters(["loggedIn"]), | ||
| ...mapState(["currentUser"]), | ||
| isAuthor: function () { | ||
| return this.currentUser === this.item.author.id; | ||
| }, | ||
| }, | ||
| }; | ||
| </script> | ||
|
|
||
| <style> | ||
| .item-title { | ||
| .post-title { | ||
| font-size: 24px; | ||
| text-align: center; | ||
| } | ||
| .item-buttons { | ||
| .post-buttons { | ||
| padding: 15px 0; | ||
| } | ||
| </style> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,75 +1,130 @@ | ||
| import { shallowMount, mount } from "@vue/test-utils"; | ||
| import { createLocalVue, mount } from "@vue/test-utils"; | ||
| import VueApollo from "vue-apollo"; | ||
| import Vuex from "vuex"; | ||
| import { createMockClient } from "mock-apollo-client"; | ||
|
Contributor
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. So it seems to have helped you, good to know! |
||
| import NewsList from "./NewsList.vue"; | ||
| import NewsItem from "../NewsItem/NewsItem.vue"; | ||
| import allPostsQuery from "../../gql/Posts.gql"; | ||
|
|
||
| const testItems = [ | ||
| { id: 1, title: "VueJS", votes: 1 }, | ||
| { id: 2, title: "TDD", votes: 4 }, | ||
| { id: 3, title: "React", votes: 0 }, | ||
| ]; | ||
| const descendingTestItems = [ | ||
| { id: 2, title: "TDD", votes: 4 }, | ||
| { id: 1, title: "VueJS", votes: 1 }, | ||
| { id: 3, title: "React", votes: 0 }, | ||
| ]; | ||
| const ascendingTestItems = [ | ||
| { id: 3, title: "React", votes: 0 }, | ||
| { id: 1, title: "VueJS", votes: 1 }, | ||
| { id: 2, title: "TDD", votes: 4 }, | ||
| ]; | ||
| const postListMock = { | ||
| data: { | ||
| posts: [ | ||
| { | ||
| id: "1", | ||
| title: "Vue", | ||
| votes: 4, | ||
| author: { | ||
| id: "1", | ||
| __typename: "User", | ||
| }, | ||
| __typename: "Post", | ||
| }, | ||
| { | ||
| id: "2", | ||
| title: "React", | ||
| votes: 0, | ||
| author: { | ||
| id: "2", | ||
| __typename: "User", | ||
| }, | ||
| __typename: "Post", | ||
| }, | ||
| { | ||
| id: "3", | ||
| title: "TDD", | ||
| votes: 2, | ||
| author: { | ||
| __typename: "User", | ||
| id: "3", | ||
| }, | ||
| __typename: "Post", | ||
| }, | ||
| ], | ||
| }, | ||
| }; | ||
|
|
||
| const localVue = createLocalVue(); | ||
| localVue.use(VueApollo); | ||
| localVue.use(Vuex); | ||
|
|
||
| describe("NewsList.vue", () => { | ||
| describe("empty", () => { | ||
| it("renders a message when the item list is empty", () => { | ||
| const wrapper = shallowMount(NewsList, { | ||
| propsData: { | ||
| initialItems: [], | ||
| let wrapper; | ||
| let mockClient; | ||
| let apolloProvider; | ||
| let requestHandlers; | ||
| let store = new Vuex.Store({ | ||
| getters: { | ||
| loggedIn: () => false, | ||
| }, | ||
| mutations: { | ||
| setToken() {}, | ||
| }, | ||
| }); | ||
|
|
||
| const createComponent = (handlers) => { | ||
| mockClient = createMockClient({ | ||
| resolvers: {}, | ||
| }); | ||
| requestHandlers = { | ||
| allPostsQueryHandler: jest.fn().mockResolvedValue({ ...postListMock }), | ||
| ...handlers, | ||
| }; | ||
| mockClient.setRequestHandler(allPostsQuery, requestHandlers.allPostsQueryHandler); | ||
| apolloProvider = new VueApollo({ defaultClient: mockClient }); | ||
| const getToken = jest.fn(); | ||
| wrapper = mount(NewsList, { | ||
| store, | ||
| localVue, | ||
| apolloProvider, | ||
| mocks: { | ||
| $apolloHelpers: { | ||
| getToken, | ||
| }, | ||
| }, | ||
| }); | ||
| }; | ||
|
|
||
| afterEach(() => { | ||
| wrapper.destroy(); | ||
| mockClient = null; | ||
| apolloProvider = null; | ||
| }); | ||
|
|
||
| it("renders a Vue component", () => { | ||
| createComponent(); | ||
| expect(wrapper.exists()).toBe(true); | ||
| expect(wrapper.vm.$apollo.queries.items).toBeTruthy(); | ||
|
Contributor
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. I don't think you need to test this. |
||
| }); | ||
|
|
||
| describe("empty", () => { | ||
| it("renders a message when the item list is empty", async () => { | ||
| createComponent({ | ||
| allPostsQueryHandler: jest.fn().mockResolvedValue({ data: { posts: [] } }), | ||
| }); | ||
| await wrapper.vm.$nextTick(); | ||
|
|
||
|
Comment on lines
+100
to
+105
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. ⭐ Refactor looks good. We also used |
||
| expect(wrapper.find("#emptyListMessage").text()).toBe("The list is empty :("); | ||
| }); | ||
| }); | ||
| describe("not empty", () => { | ||
| let wrapper; | ||
| beforeEach(() => { | ||
| wrapper = mount(NewsList, { | ||
| propsData: { | ||
| initialItems: testItems, | ||
| }, | ||
| }); | ||
| beforeEach(async () => { | ||
| createComponent(); | ||
| await wrapper.vm.$nextTick(); | ||
| }); | ||
| it("does not render empty list message when item list is filled", () => { | ||
| expect(wrapper.find("#emptyListMessage").exists()).toBe(false); | ||
| }); | ||
| it("orderedItems sorts items in descending order by default", () => { | ||
| let localThis = { | ||
| items: testItems, | ||
| descending: true, | ||
| }; | ||
| expect(NewsList.computed.orderedItems.call(localThis)).toEqual(descendingTestItems); | ||
| }); | ||
| it("renders items in descending order by default", () => { | ||
| let newsItems = wrapper.findAllComponents(NewsItem); | ||
| expect(newsItems.wrappers.map((i) => i.props("item").title)).toEqual( | ||
| descendingTestItems.map((i) => i.title) | ||
| ); | ||
| expect(newsItems.wrappers.map((i) => i.props("item").title)).toEqual(["Vue", "TDD", "React"]); | ||
|
Contributor
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. Much better to read. |
||
| }); | ||
| describe("Reverse order", () => { | ||
| it("orderedItems sorts items in ascending order", () => { | ||
| let localThis = { | ||
| items: testItems, | ||
| descending: false, | ||
| }; | ||
| expect(NewsList.computed.orderedItems.call(localThis)).toEqual(ascendingTestItems); | ||
| }); | ||
| describe("click 'Reverse Order'", () => { | ||
| it("renders items in ascending order", async () => { | ||
| let reverseOrderButton = wrapper.find("#reverseOrder"); | ||
| await reverseOrderButton.trigger("click"); | ||
| let newsItems = wrapper.findAllComponents(NewsItem); | ||
| expect(newsItems.wrappers.map((i) => i.props("item").title)).toEqual( | ||
| ascendingTestItems.map((i) => i.title) | ||
| ); | ||
| expect(newsItems.wrappers.map((i) => i.props("item").title)).toEqual(["React", "TDD", "Vue"]); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
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.