Skip to content
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

Initial implementation of Spotifiy API connection, including a test suite #18

Merged
merged 6 commits into from
Nov 16, 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 .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ jobs:
- run: yarn lint
- run: yarn test
env:
REACT_APP_SPOTIFY_CLIENT_ID: ${{ secrets.REACT_APP_SPOTIFY_CLIENT_ID }}
REACT_APP_SPOTIFY_CLIENT_SECRET: ${{ secrets.REACT_APP_SPOTIFY_CLIENT_SECRET }}
REACT_APP_SPOTIFY_CLIENT_ID: "TEST_REACT_APP_SPOTIFY_CLIENT_ID"
REACT_APP_SPOTIFY_CLIENT_SECRET: "TEST_REACT_APP_SPOTIFY_CLIENT_SECRET"
REACT_APP_SPOTIFY_REDIRECT_URI: "http://localhost:3000/goldify"
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/.vscode
/node_modules
/.pnp
.pnp.js
.env
.yarnrc
.yarn/*

# testing
/coverage
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
"devDependencies": {
"babel-eslint": "^10.1.0",
"eslint-plugin-react": "^7.21.5",
"lint": "^0.7.0"
"lint": "^0.7.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.5",
"axios": "^0.21.0"
}
}
47 changes: 44 additions & 3 deletions src/__tests__/GoldifyApp.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,51 @@
import React from 'react';
import React from "react";
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import userEvent from '@testing-library/user-event';
import GoldifyApp from '../js/GoldifyApp';
import { configure } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import reportWebVitals from '../utils/reportWebVitals';

test('renders learn react link', () => {
configure({adapter: new Adapter()});

test("Goldify Header exists on main app", () => {
render(<GoldifyApp />);
const linkElement = screen.getByText(/Goldify/i);
const linkElement = screen.getByText(/Home/i);
expect(linkElement).toBeInTheDocument();
});

test("Content changes on click of Get Started", () => {
render(<GoldifyApp />);
const homeBody = screen.getByText(/Welcome to Goldify!/i);
expect(homeBody).toBeInTheDocument();

const leftClick = { button: 0 }
const getStartedButton = screen.getByText("Get Started").closest("a");
const homeButton = screen.getByText("Home").closest("a");
expect(getStartedButton).toHaveAttribute("href", "/goldify");
expect(homeButton).toHaveAttribute("href", "/");

userEvent.click(getStartedButton, leftClick);

const viewProfile = screen.getByText(/Loading.../i);
expect(homeBody).not.toBeInTheDocument();
expect(viewProfile).toBeInTheDocument();
});

test("Run Web Vitals", () => {
reportWebVitals(console.log);

reportWebVitals(
(metric) => {
console.log(metric);
}
);
});

test("Load index js script file without error", () => {
const root = document.createElement("div");
root.id = "root";
document.body.appendChild(root);
require("../index.js");
});
172 changes: 172 additions & 0 deletions src/__tests__/GoldifyExecutePage.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import React from "react";
import '@testing-library/jest-dom';
import { configure, shallow } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import GoldifyExecutePage from '../js/GoldifyExecutePage';
import {
retrieveTokensAxios,
retrieveUserDataAxios,
retrieveAuthenticationCode,
replaceWindowURL
} from '../utils/GoldifyExecuteUtils';

jest.mock('../utils/GoldifyExecuteUtils', () => ({
retrieveAuthenticationCode: jest.fn(),
retrieveAuthorization: jest.fn(),
retrieveTokensAxios: jest.fn(),
retrieveUserDataAxios: jest.fn(),
replaceWindowURL: jest.fn(),
}));

configure({ adapter: new Adapter() });

const goldifyExecuteTestUtils = require("../utils/GoldifyExecuteTestUtils");

test("Confirm authorization code in componentDidMount is sent to retrieveDataOnPageLoad", () => {
const wrapper = shallow(<GoldifyExecutePage />);
wrapper.instance().retrieveDataOnPageLoad = jest.fn();

retrieveAuthenticationCode.mockReturnValue(goldifyExecuteTestUtils.testAuthenticationCode);

wrapper.instance().componentDidMount();
expect(wrapper.instance().retrieveDataOnPageLoad).toHaveBeenCalledTimes(1);
expect(wrapper.instance().retrieveDataOnPageLoad).toHaveBeenCalledWith(
goldifyExecuteTestUtils.testAuthenticationCode
);
});

test("Test GoldifyExecutePage functionality: retrieveDataOnPageLoad", async () => {
retrieveTokensAxios.mockImplementation(() => Promise.resolve(
goldifyExecuteTestUtils.getTokensTestData()
));

const wrapper = shallow(<GoldifyExecutePage />);
wrapper.instance().retrieveUserData = jest.fn();
await wrapper.instance().retrieveDataOnPageLoad(goldifyExecuteTestUtils.testAuthenticationCode);
expect(wrapper.instance().retrieveUserData).toHaveBeenCalledTimes(1);
expect(wrapper.instance().retrieveUserData).toHaveBeenCalledWith(
goldifyExecuteTestUtils.testAuthenticationCode,
goldifyExecuteTestUtils.getTokensTestData()
);
});

test("Expect home page to load when running retrieveDataOnPageLoad with bad data", async () => {
retrieveTokensAxios.mockImplementation(() => Promise.resolve());

const wrapper = shallow(<GoldifyExecutePage />);
await wrapper.instance().retrieveDataOnPageLoad(goldifyExecuteTestUtils.testAuthenticationCode);
expect(replaceWindowURL).toHaveBeenCalledTimes(1);
expect(replaceWindowURL).toHaveBeenCalledWith("/");
});

test("Test GoldifyExecutePage functionality: retrieveUserData", async () => {
retrieveUserDataAxios.mockImplementation(() => Promise.resolve(
goldifyExecuteTestUtils.getUserTestData()
));

const wrapper = shallow(<GoldifyExecutePage />);
wrapper.instance().setStateTokensAndUserData = jest.fn();
await wrapper.instance().retrieveUserData(
goldifyExecuteTestUtils.testAuthenticationCode,
goldifyExecuteTestUtils.getTokensTestData()
);
expect(wrapper.instance().setStateTokensAndUserData).toHaveBeenCalledTimes(1);
expect(wrapper.instance().setStateTokensAndUserData).toHaveBeenCalledWith(
goldifyExecuteTestUtils.testAuthenticationCode,
goldifyExecuteTestUtils.getTokensTestData(),
goldifyExecuteTestUtils.getUserTestData()
);
});

test("Expect home page to load when running retrieveUserData with bad data", async () => {
retrieveUserDataAxios.mockImplementation(() => Promise.resolve());

const wrapper = shallow(<GoldifyExecutePage />);
await wrapper.instance().retrieveUserData(
goldifyExecuteTestUtils.testAuthenticationCode,
goldifyExecuteTestUtils.getTokensTestData()
);
expect(replaceWindowURL).toHaveBeenCalledTimes(1);
expect(replaceWindowURL).toHaveBeenCalledWith("/");
});

test("Confirm state is altered after calling setStateTokensAndUserData", () => {
const wrapper = shallow(<GoldifyExecutePage />);
expect(wrapper.instance().state).toEqual({
authorizationCode: "",
refreshToken: "",
accessToken: "",
userData: null,
userDataString: ""
});
wrapper.instance().setStateTokensAndUserData(
goldifyExecuteTestUtils.testAuthenticationCode,
goldifyExecuteTestUtils.getTokensTestData(),
goldifyExecuteTestUtils.getUserTestData()
);
expect(wrapper.instance().state).toEqual({
authorizationCode: goldifyExecuteTestUtils.testAuthenticationCode,
refreshToken: goldifyExecuteTestUtils.getTokensTestData().refresh_token,
accessToken: goldifyExecuteTestUtils.getTokensTestData().access_token,
userData: goldifyExecuteTestUtils.getUserTestData(),
userDataString: JSON.stringify(goldifyExecuteTestUtils.getUserTestData())
});
});

test("Check for Loading... in loading page", () => {
const wrapper = shallow(<GoldifyExecutePage />);
let loadingPageString = JSON.stringify(wrapper.instance().getLoadingPage());
expect(loadingPageString).toContain("Loading...");
});

test("Confirm an error occurs when attempting to grab the user data page without setting the user data", () => {
const wrapper = shallow(<GoldifyExecutePage />);
let errorThrown = false;
try {
wrapper.instance().getUserDataPage();
} catch (err) {
expect(err).toEqual(TypeError("Cannot read property 'images' of null"));
errorThrown = true;
}
expect(errorThrown).toBe(true);
});

test("Check for user data in user data page after setting the state", () => {
const wrapper = shallow(<GoldifyExecutePage />);
wrapper.instance().state = {
authorizationCode: goldifyExecuteTestUtils.testAuthenticationCode,
refreshToken: goldifyExecuteTestUtils.getTokensTestData().refresh_token,
accessToken: goldifyExecuteTestUtils.getTokensTestData().access_token,
userData: goldifyExecuteTestUtils.getUserTestData(),
userDataString: JSON.stringify(goldifyExecuteTestUtils.getUserTestData())
};
let userDataString = JSON.stringify(wrapper.instance().getUserDataPage());
expect(userDataString).toContain(goldifyExecuteTestUtils.testUserImageURL);
expect(userDataString).toContain(goldifyExecuteTestUtils.testUserFollowersTotal);
expect(userDataString).toContain(goldifyExecuteTestUtils.testUserExternalUrlSpotify);
expect(userDataString).toContain(goldifyExecuteTestUtils.testUserCountry);
expect(userDataString).toContain(goldifyExecuteTestUtils.testUserId);
expect(userDataString).toContain(goldifyExecuteTestUtils.testUserDisplayName);
expect(userDataString).toContain(goldifyExecuteTestUtils.testUserEmail);
});

test("Confirm the functionality of tokensAndOrDataAreInvalid", () => {
const wrapper = shallow(<GoldifyExecutePage />);
expect(wrapper.instance().tokensAndOrDataAreInvalid()).toBe(true);
wrapper.instance().state.authorizationCode = goldifyExecuteTestUtils.testAuthenticationCode;
expect(wrapper.instance().tokensAndOrDataAreInvalid()).toBe(true);
wrapper.instance().state.accessToken = goldifyExecuteTestUtils.testAccessToken;
expect(wrapper.instance().tokensAndOrDataAreInvalid()).toBe(true);
wrapper.instance().state.userData = goldifyExecuteTestUtils.getUserTestData();
expect(wrapper.instance().tokensAndOrDataAreInvalid()).toBe(false);
});

test("Check for which page is loaded on render based on tokensAndOrDataAreInvalid", () => {
const wrapper = shallow(<GoldifyExecutePage />);
wrapper.instance().getLoadingPage = jest.fn().mockReturnValue("Loading Page!");
wrapper.instance().getUserDataPage = jest.fn().mockReturnValue("User Data Page!");
wrapper.instance().tokensAndOrDataAreInvalid = jest.fn().mockReturnValue(true);
expect(wrapper.instance().render()).toEqual("Loading Page!");
wrapper.instance().tokensAndOrDataAreInvalid = jest.fn().mockReturnValue(false);
expect(wrapper.instance().render()).toEqual("User Data Page!");
});
101 changes: 101 additions & 0 deletions src/__tests__/GoldifyExecuteUtils.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import '@testing-library/jest-dom';
import axios from "axios";
import qs from "qs"
import {
clientId,
redirectUri,
generateRandomString,
retrieveSpotifyApiScopesNeeded,
retrieveSpotifyApiAuthorizeURL,
retrieveAuthorization,
retrieveAuthenticationCode,
retrieveTokensAxios,
retrieveUserDataAxios,
replaceWindowURL
} from '../utils/GoldifyExecuteUtils';

jest.mock('axios');

const goldifyExecuteTestUtils = require("../utils/GoldifyExecuteTestUtils");

test("Function to generate random function is random", () => {
let randomStr1 = generateRandomString(16);
let randomStr2 = generateRandomString(16);
let randomStr3 = generateRandomString(16);
expect(randomStr1).not.toEqual(randomStr2);
expect(randomStr2).not.toEqual(randomStr3);
expect(randomStr1).not.toEqual(randomStr3);
});

test("The Spotify API scopes string includes all scopes needed for Goldify", () => {
let spotifyApiScope = retrieveSpotifyApiScopesNeeded();
expect(spotifyApiScope).toContain("user-read-private");
expect(spotifyApiScope).toContain("user-read-email");
});

test("The Spotify API Authorization URL has correct components in it", () => {
let spotifyApiAuthURL = retrieveSpotifyApiAuthorizeURL();
expect(spotifyApiAuthURL).toContain("https://accounts.spotify.com/authorize?");
expect(spotifyApiAuthURL).toContain("response_type=code");
expect(spotifyApiAuthURL).toContain("client_id=" + qs.stringify(clientId));
expect(spotifyApiAuthURL).toContain("scope=");
expect(spotifyApiAuthURL).toContain("redirect_uri=" + qs.stringify(redirectUri));
expect(spotifyApiAuthURL).toContain("state=");
});

test("The function retrieveAuthorization attempts to replace the window with the Spotify API URL", () => {
retrieveAuthorization();
expect(window.location.replace).toHaveBeenCalledTimes(1);
});

test("Landing page should render null authentication code", () => {
expect(retrieveAuthenticationCode()).toEqual(null);
});

test("Check for to make sure retrieveTokensAxios returns correct mock data", async () => {
axios.post.mockResolvedValue({
data: goldifyExecuteTestUtils.getTokensTestData()
});

const responseData = await retrieveTokensAxios();
expect(responseData).toEqual(goldifyExecuteTestUtils.getTokensTestData());
});

test("Check for to make sure retrieveTokensAxios throws error on bad data", async () => {
axios.post.mockResolvedValue(null);
console.log = jest.fn();
await retrieveTokensAxios();
expect(console.log).toHaveBeenCalledWith(TypeError("Cannot read property 'data' of null"));

axios.post.mockResolvedValue(undefined);
console.log = jest.fn();
await retrieveTokensAxios();
expect(console.log).toHaveBeenCalledWith(TypeError("Cannot read property 'data' of undefined"));
});

test("Check for to make sure retrieveUserDataAxios returns correct mock data", async () => {
axios.get.mockResolvedValue({
data: goldifyExecuteTestUtils.getUserTestData()
});

const responseData = await retrieveUserDataAxios(goldifyExecuteTestUtils.getTokensTestData());
expect(responseData).toEqual(goldifyExecuteTestUtils.getUserTestData());
});

test("Check for to make sure retrieveUserDataAxios throws error on bad data", async () => {
axios.get.mockResolvedValue(null);
console.log = jest.fn();
await retrieveUserDataAxios(goldifyExecuteTestUtils.getTokensTestData());
expect(console.log).toHaveBeenCalledWith(TypeError("Cannot read property 'data' of null"));

axios.get.mockResolvedValue(undefined);
console.log = jest.fn();
await retrieveUserDataAxios(goldifyExecuteTestUtils.getTokensTestData());
expect(console.log).toHaveBeenCalledWith(TypeError("Cannot read property 'data' of undefined"));
});

test("Confirm replaceWindowURL replaces the window with the given URL", async () => {
replaceWindowURL("TEST_URL");
expect(window.location.replace).toHaveBeenCalledTimes(1);
expect(window.location.replace).toHaveBeenCalledWith("TEST_URL");
});
Loading