diff --git a/src/Router.jsx b/src/Router.jsx index 0475d489..06f9081f 100644 --- a/src/Router.jsx +++ b/src/Router.jsx @@ -7,6 +7,8 @@ import PrivateRoute from "./components/PrivateRoute"; import { routes } from "./constants"; import RegisterPage from "./pages/RegisterPage"; import ProfilePage from "./pages/ProfilePage"; +import TestVariationListPage from "./pages/TestVariationListPage"; +import TestVariationDetailsPage from "./pages/TestVariationDetailsPage"; function Router() { return ( @@ -28,6 +30,16 @@ function Router() { path={`${routes.HOME}:projectId`} component={() => } /> + } + /> + } + /> + `${routes.HOME}${testVariation.projectId}?buildId=${testRun.buildId}&testId=${testRun.id}` export const buildTestRunLocation = (testRun: TestRun) => ({ search: `buildId=${testRun.buildId}&testId=${testRun.id}`, diff --git a/src/components/Filters.tsx b/src/components/Filters.tsx index 8bfca33d..f47d001a 100644 --- a/src/components/Filters.tsx +++ b/src/components/Filters.tsx @@ -35,15 +35,15 @@ const Filters: React.FunctionComponent = ({ const [testStatus, setTestStatus] = testStatusState; const osList = testRuns - .map((t) => t.testVariation.os) + .map((t) => t.os) .filter((v, i, array) => v && array.indexOf(v) === i); const browserList = testRuns - .map((t) => t.testVariation.browser) + .map((t) => t.browser) .filter((v, i, array) => v && array.indexOf(v) === i); const viewportList = testRuns - .map((t) => t.testVariation.viewport) + .map((t) => t.viewport) .filter((v, i, array) => v && array.indexOf(v) === i); const testStatusList = testRuns diff --git a/src/components/TestDetailsModal.tsx b/src/components/TestDetailsModal.tsx index 79eed11c..9ced1df1 100644 --- a/src/components/TestDetailsModal.tsx +++ b/src/components/TestDetailsModal.tsx @@ -12,7 +12,7 @@ import { } from "@material-ui/core"; import { TestRun } from "../types"; import { createStyles, makeStyles, Theme } from "@material-ui/core/styles"; -import { testsService } from "../services"; +import { testRunService, testVariationService } from "../services"; import DrawArea from "./DrawArea"; import { TestStatus } from "../types/testStatus"; import { useHistory, Prompt } from "react-router-dom"; @@ -36,17 +36,15 @@ const TestDetailsModal: React.FunctionComponent<{ const history = useHistory(); const classes = useStyles(); - const [isDiffShown, setIsDiffShown] = useState( - testRun.status === TestStatus.unresolved - ); + const [isDiffShown, setIsDiffShown] = useState(!!testRun.diffName); const [selectedRectId, setSelectedRectId] = React.useState(); const [ignoreAreas, setIgnoreAreas] = React.useState( - JSON.parse(testRun.testVariation.ignoreAreas) + JSON.parse(testRun.ignoreAreas) ); React.useEffect(() => { - setIgnoreAreas(JSON.parse(testRun.testVariation.ignoreAreas)); + setIgnoreAreas(JSON.parse(testRun.ignoreAreas)); }, [testRun]); const removeSelection = (event: KonvaEventObject) => { @@ -72,7 +70,7 @@ const TestDetailsModal: React.FunctionComponent<{ }; const isIgnoreAreasSaved = () => { - return testRun.testVariation.ignoreAreas === JSON.stringify(ignoreAreas); + return testRun.ignoreAreas === JSON.stringify(ignoreAreas); }; return ( @@ -85,24 +83,22 @@ const TestDetailsModal: React.FunctionComponent<{ - {testRun.testVariation.name} + {testRun.name} + + + setIsDiffShown(!isDiffShown)} + name="Show diff" + /> - {testRun.status === TestStatus.unresolved && ( - - setIsDiffShown(!isDiffShown)} - name="Show diff" - /> - - )} {(testRun.status === TestStatus.unresolved || testRun.status === TestStatus.new) && ( + + + + ))} + + ); +}; + +export default TestVariationList; diff --git a/src/constants/routes.ts b/src/constants/routes.ts index 9fee3262..8698ab9e 100644 --- a/src/constants/routes.ts +++ b/src/constants/routes.ts @@ -7,5 +7,6 @@ export const routes = { HOME: "/", PROFILE_PAGE: "/profile", PROJECT_LIST_PAGE: "/projects", - // PROJECT: "/project", + VARIATION_LIST_PAGE: "/variations", + VARIATION_DETAILS_PAGE: "/variations/details", }; diff --git a/src/pages/ProjectListPage.tsx b/src/pages/ProjectListPage.tsx index c0e89a42..958607b5 100644 --- a/src/pages/ProjectListPage.tsx +++ b/src/pages/ProjectListPage.tsx @@ -3,7 +3,6 @@ import { Grid, Typography, Card, - CardActionArea, IconButton, CardContent, CardActions, @@ -27,6 +26,7 @@ import { } from "../contexts/project.context"; import { Link } from "react-router-dom"; import { Delete, Add } from "@material-ui/icons"; +import { routes } from "../constants"; const ProjectsListPage = () => { const theme = useTheme(); @@ -114,14 +114,24 @@ const ProjectsListPage = () => { Key: {project.id} + Name: {project.name} + Updated: {project.updatedAt} - - - Name: {project.name} - Updated: {project.updatedAt} - - + + ) => { deleteProject(projectDispatch, project.id); diff --git a/src/pages/ProjectPage.tsx b/src/pages/ProjectPage.tsx index 92a50665..53dd5471 100644 --- a/src/pages/ProjectPage.tsx +++ b/src/pages/ProjectPage.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useState } from "react"; -import { Grid, Dialog, IconButton, Box } from "@material-ui/core"; +import { Grid, Dialog, IconButton, Box, Typography } from "@material-ui/core"; import { useParams, useLocation, useHistory } from "react-router-dom"; import { Build, TestRun } from "../types"; -import { projectsService, buildsService, testsService } from "../services"; +import { buildsService, testRunService } from "../services"; import BuildList from "../components/BuildList"; import ProjectSelect from "../components/ProjectSelect"; import qs from "qs"; @@ -79,13 +79,13 @@ const ProjectPage = () => { useEffect(() => { if (projectId) { - projectsService.getBuilds(projectId).then((builds) => setBuilds(builds)); + buildsService.getList(projectId).then((builds) => setBuilds(builds)); } }, [projectId]); useEffect(() => { if (selectedBuildId) { - buildsService.getTestRuns(selectedBuildId).then((testRuns: TestRun[]) => { + testRunService.getList(selectedBuildId).then((testRuns) => { setTestRuns(testRuns); }); } @@ -95,11 +95,11 @@ const ProjectPage = () => { setFilteredTestRuns( testRuns.filter( (t) => - t.testVariation.name.includes(query) && // by query - (os ? t.testVariation.os === os : true) && // by OS - (viewport ? t.testVariation.viewport === viewport : true) && // by viewport + t.name.includes(query) && // by query + (os ? t.os === os : true) && // by OS + (viewport ? t.viewport === viewport : true) && // by viewport (testStatus ? t.status === testStatus : true) && // by status - (browser ? t.testVariation.browser === browser : true) // by browser + (browser ? t.browser === browser : true) // by browser ) ); }, [query, os, browser, viewport, testStatus, testRuns]); @@ -119,6 +119,7 @@ const ProjectPage = () => { + Project: @@ -149,7 +150,7 @@ const ProjectPage = () => { items={filteredTestRuns} selectedId={selectedTestdId} handleRemove={(id: string) => - testsService.remove(id).then((isRemoved) => { + testRunService.remove(id).then((isRemoved) => { if (isRemoved) { setTestRuns(testRuns.filter((item) => item.id !== id)); } diff --git a/src/pages/TestVariationDetailsPage.tsx b/src/pages/TestVariationDetailsPage.tsx new file mode 100644 index 00000000..a14e978c --- /dev/null +++ b/src/pages/TestVariationDetailsPage.tsx @@ -0,0 +1,84 @@ +import React from "react"; +import { useParams, Link } from "react-router-dom"; +import { TestVariation } from "../types"; +import { testVariationService, staticService } from "../services"; +import { + Container, + Box, + Grid, + Typography, + Card, + CardMedia, + makeStyles, + CardActions, + Button, +} from "@material-ui/core"; +import { buildTestRunUrl } from "../_helpers/route.helpers"; + +const useStyles = makeStyles({ + media: { + height: 600, + backgroundSize: "contain", + }, +}); + +const TestVariationDetailsPage: React.FunctionComponent = () => { + const classes = useStyles(); + const { testVariationId } = useParams(); + const [testVariation, setTestVariation] = React.useState(); + + React.useEffect(() => { + if (testVariationId) { + testVariationService.getDetails(testVariationId).then((item) => { + setTestVariation(item); + }); + } + }, [testVariationId]); + + return ( + + + + {testVariation && ( + + {testVariation.name} + + + + OS: {testVariation.os} + + + Browser: {testVariation.browser} + + + Viewport: {testVariation.viewport} + + + {testVariation.baselines.map((baseline) => ( + + + + + + + + + ))} + + )} + + + + ); +}; + +export default TestVariationDetailsPage; diff --git a/src/pages/TestVariationListPage.tsx b/src/pages/TestVariationListPage.tsx new file mode 100644 index 00000000..4299fc42 --- /dev/null +++ b/src/pages/TestVariationListPage.tsx @@ -0,0 +1,42 @@ +import React from "react"; +import TestVariationList from "../components/TestVariationList"; +import { useParams } from "react-router-dom"; +import { TestVariation } from "../types"; +import { testVariationService } from "../services"; +import { Container, Box, Grid, Typography } from "@material-ui/core"; +import ProjectSelect from "../components/ProjectSelect"; + +const TestVariationListPage: React.FunctionComponent = () => { + const { projectId } = useParams(); + const [testVariations, setTestVariations] = React.useState( + [] + ); + + React.useEffect(() => { + if (projectId) { + testVariationService.getList(projectId).then((testVariations) => { + setTestVariations(testVariations); + }); + } + }, [projectId]); + + return ( + + + + + + Project: + + + + + + + + + + ); +}; + +export default TestVariationListPage; diff --git a/src/services/builds.service.ts b/src/services/builds.service.ts index f986c22d..801e96d2 100644 --- a/src/services/builds.service.ts +++ b/src/services/builds.service.ts @@ -1,19 +1,23 @@ -import { TestRun } from "../types"; +import { Build } from "../types"; import { handleResponse, authHeader } from "../_helpers/service.helpers"; import { API_URL } from "../_config/api.config"; +const ENDPOINT_URL = "/builds" + export const buildsService = { - getTestRuns, + getList, remove, }; -function getTestRuns(id: string): Promise { +function getList(projectId: string): Promise { const requestOptions = { method: "GET", headers: authHeader(), }; - return fetch(`${API_URL}/builds/${id}`, requestOptions).then(handleResponse); + return fetch(`${API_URL}${ENDPOINT_URL}?projectId=${projectId}`, requestOptions).then( + handleResponse + ); } function remove(id: string): Promise { @@ -22,5 +26,5 @@ function remove(id: string): Promise { headers: authHeader(), }; - return fetch(`${API_URL}/builds/${id}`, requestOptions).then(handleResponse); + return fetch(`${API_URL}${ENDPOINT_URL}/${id}`, requestOptions).then(handleResponse); } diff --git a/src/services/index.ts b/src/services/index.ts index 4e28d927..8131c1de 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -1,5 +1,6 @@ export * from './users.service'; export * from './projects.service' export * from './builds.service' -export * from './tests.service' -export * from './static.service' \ No newline at end of file +export * from './static.service' +export * from './testVariation.service' +export * from './testRun.service' \ No newline at end of file diff --git a/src/services/projects.service.ts b/src/services/projects.service.ts index aad528a5..170287a8 100644 --- a/src/services/projects.service.ts +++ b/src/services/projects.service.ts @@ -1,9 +1,8 @@ -import { Project, Build } from "../types"; +import { Project } from "../types"; import { handleResponse, authHeader } from "../_helpers/service.helpers"; import { API_URL } from "../_config/api.config"; export const projectsService = { - getBuilds, getAll, remove, create, @@ -18,17 +17,6 @@ function getAll(): Promise { return fetch(`${API_URL}/projects`, requestOptions).then(handleResponse); } -function getBuilds(id: string): Promise { - const requestOptions = { - method: "GET", - headers: authHeader(), - }; - - return fetch(`${API_URL}/projects/${id}`, requestOptions).then( - handleResponse - ); -} - function remove(id: string): Promise { const requestOptions = { method: "DELETE", diff --git a/src/services/tests.service.ts b/src/services/testRun.service.ts similarity index 65% rename from src/services/tests.service.ts rename to src/services/testRun.service.ts index a840ba70..f7fb37db 100644 --- a/src/services/tests.service.ts +++ b/src/services/testRun.service.ts @@ -2,62 +2,61 @@ import { TestRun } from "../types"; import { handleResponse, authHeader } from "../_helpers/service.helpers"; import { API_URL } from "../_config/api.config"; import { IgnoreArea } from "../types/ignoreArea"; -import { TestVariation } from "../types/testVariation"; -export const testsService = { - get, +const ENDPOINT_URL = "/test-runs" + +export const testRunService = { + getList, + remove, approve, reject, setIgnoreAreas, - remove, }; -function get(testId: string): Promise { +function getList(buildId: string): Promise { const requestOptions = { method: "GET", headers: authHeader(), }; - return fetch(`${API_URL}/test/${testId}`, requestOptions).then( - handleResponse - ); + return fetch(`${API_URL}${ENDPOINT_URL}?buildId=${buildId}`, requestOptions).then(handleResponse); } -function approve(id: string): Promise { +function remove(id: string): Promise { const requestOptions = { - method: "GET", + method: "DELETE", headers: authHeader(), }; - return fetch(`${API_URL}/test/approve/${id}`, requestOptions).then( - handleResponse - ); + return fetch(`${API_URL}${ENDPOINT_URL}/${id}`, requestOptions).then(handleResponse); } -function reject(id: string): Promise { +function approve(id: string): Promise { const requestOptions = { method: "GET", headers: authHeader(), }; - return fetch(`${API_URL}/test/reject/${id}`, requestOptions).then( + return fetch(`${API_URL}${ENDPOINT_URL}/approve/${id}`, requestOptions).then( handleResponse ); } -function remove(id: string): Promise { +function reject(id: string): Promise { const requestOptions = { - method: "DELETE", + method: "GET", headers: authHeader(), }; - return fetch(`${API_URL}/test/${id}`, requestOptions).then(handleResponse); + return fetch(`${API_URL}${ENDPOINT_URL}/reject/${id}`, requestOptions).then( + handleResponse + ); } function setIgnoreAreas( - variationId: string, + id: string, ignoreAreas: IgnoreArea[] -): Promise { +): Promise { const requestOptions = { method: "PUT", headers: { "Content-Type": "application/json", ...authHeader() }, @@ -65,7 +64,7 @@ function setIgnoreAreas( }; return fetch( - `${API_URL}/test/ignoreArea/${variationId}`, + `${API_URL}${ENDPOINT_URL}/ignoreArea/${id}`, requestOptions ).then(handleResponse); -} +} \ No newline at end of file diff --git a/src/services/testVariation.service.ts b/src/services/testVariation.service.ts new file mode 100644 index 00000000..7574fc51 --- /dev/null +++ b/src/services/testVariation.service.ts @@ -0,0 +1,46 @@ +import { TestVariation } from "../types"; +import { handleResponse, authHeader } from "../_helpers/service.helpers"; +import { API_URL } from "../_config/api.config"; +import { IgnoreArea } from "../types/ignoreArea"; + +const ENDPOINT_URL = "/test-variations" + +export const testVariationService = { + getList, + getDetails, + setIgnoreAreas +}; + +function getList(projectId: String): Promise { + const requestOptions = { + method: "GET", + headers: authHeader(), + }; + + return fetch(`${API_URL}${ENDPOINT_URL}?projectId=${projectId}`, requestOptions).then(handleResponse); +} + +function getDetails(id: String): Promise { + const requestOptions = { + method: "GET", + headers: authHeader(), + }; + + return fetch(`${API_URL}${ENDPOINT_URL}/${id}`, requestOptions).then(handleResponse); +} + +function setIgnoreAreas( + variationId: string, + ignoreAreas: IgnoreArea[] +): Promise { + const requestOptions = { + method: "PUT", + headers: { "Content-Type": "application/json", ...authHeader() }, + body: JSON.stringify(ignoreAreas), + }; + + return fetch( + `${API_URL}${ENDPOINT_URL}/ignoreArea/${variationId}`, + requestOptions + ).then(handleResponse); +} \ No newline at end of file diff --git a/src/services/users.service.ts b/src/services/users.service.ts index c180cc6d..d9bc5657 100644 --- a/src/services/users.service.ts +++ b/src/services/users.service.ts @@ -2,6 +2,8 @@ import { handleResponse, authHeader } from "../_helpers/service.helpers"; import { User } from "../types/user"; import { API_URL } from "../_config/api.config"; +const ENDPOINT_URL = "/users" + export const usersService = { login, logout, @@ -17,7 +19,7 @@ function login(email: string, password: string): Promise { body: JSON.stringify({ email, password }), }; - return fetch(`${API_URL}/users/login`, requestOptions) + return fetch(`${API_URL}${ENDPOINT_URL}/login`, requestOptions) .then(handleResponse) .then((user) => { setUserInLocalStorage(user) @@ -32,7 +34,7 @@ function register(firstName: string, lastName: string, email: string, password: body: JSON.stringify({ firstName, lastName, email, password }), }; - return fetch(`${API_URL}/users/register`, requestOptions) + return fetch(`${API_URL}${ENDPOINT_URL}/register`, requestOptions) .then(handleResponse) .then((user) => { setUserInLocalStorage(user) @@ -47,7 +49,7 @@ function update({ id, firstName, lastName, email }: { id: string, firstName: str body: JSON.stringify({ firstName, lastName, email }), }; - return fetch(`${API_URL}/users/${id}`, requestOptions) + return fetch(`${API_URL}${ENDPOINT_URL}`, requestOptions) .then(handleResponse) .then((user) => { setUserInLocalStorage(user) @@ -62,7 +64,7 @@ function changePassword(password: string): Promise { body: JSON.stringify({ password }), }; - return fetch(`${API_URL}/users/password`, requestOptions) + return fetch(`${API_URL}${ENDPOINT_URL}/password`, requestOptions) .then(handleResponse) } diff --git a/src/types/baseline.ts b/src/types/baseline.ts new file mode 100644 index 00000000..8e2ac758 --- /dev/null +++ b/src/types/baseline.ts @@ -0,0 +1,11 @@ +import { TestRun } from "./testRun"; + +export interface Baseline { + id: string; + baselineName: string; + testRunId: string; + testVariationId: string; + createdAt: Date; + updatedAt: Date; + testRun: TestRun; +} diff --git a/src/types/index.ts b/src/types/index.ts index eaf1596d..e3d6c938 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,4 +1,5 @@ export * from './user' export * from './project' export * from './build' -export * from './testRun' \ No newline at end of file +export * from './testRun' +export * from './testVariation' \ No newline at end of file diff --git a/src/types/testRun.ts b/src/types/testRun.ts index 5ddc9710..7030c111 100644 --- a/src/types/testRun.ts +++ b/src/types/testRun.ts @@ -1,5 +1,4 @@ import { TestStatus } from "./testStatus"; -import { TestVariation } from "./testVariation"; export interface TestRun { id: string; @@ -9,5 +8,12 @@ export interface TestRun { diffPercent: number; diffTollerancePercent: number; status: TestStatus; - testVariation: TestVariation; + testVariationId: string; + name: string; + baselineName: string; + os: string; + browser: string; + viewport: string; + device: string; + ignoreAreas: string; } diff --git a/src/types/testVariation.ts b/src/types/testVariation.ts index 30c9b78f..2a75a30e 100644 --- a/src/types/testVariation.ts +++ b/src/types/testVariation.ts @@ -1,4 +1,4 @@ -import { IgnoreArea } from "./ignoreArea"; +import { Baseline } from "./baseline"; export interface TestVariation { id: string; @@ -9,4 +9,6 @@ export interface TestVariation { viewport: string; device: string; ignoreAreas: string; + projectId: string; + baselines: Baseline[] }