-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9790e87
commit adacfe8
Showing
25 changed files
with
1,323 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
// https://github.com/pimterry/loglevel | ||
import * as log from 'loglevel'; | ||
|
||
import { Dispatch } from '../store'; | ||
|
||
declare global { | ||
interface Window { | ||
_env_: any; | ||
} | ||
} | ||
|
||
interface TestingStates { | ||
state: any; | ||
reducers: any; | ||
effects: any; | ||
} | ||
|
||
export const testingCases: TestingStates = { | ||
state: { | ||
log: {}, | ||
loading: false, | ||
selectedTab: 'explore', | ||
dataset: 'testingCases', | ||
|
||
query: {}, | ||
queries: [], | ||
|
||
tablePaginationRowsPerPage: 25, | ||
tablePaginationCurrentPage: 0, | ||
tablePaginationOffset: 0, | ||
tablePaginationLimit: 25, | ||
defaultPoints: false, | ||
}, | ||
reducers: { | ||
setLog(state: any, payload: any) { | ||
return { ...state, log: payload }; | ||
}, | ||
setLoading(state: any, payload: any) { | ||
return { ...state, loading: payload }; | ||
}, | ||
setSelectedTab(state: any, payload: any) { | ||
return { ...state, selectedTab: payload }; | ||
}, | ||
setTablePaginationRowsPerPage(state: any, payload: any) { | ||
// Whenever we change the number of rows per page, we also reset all to default | ||
return { | ||
...state, | ||
tablePaginationRowsPerPage: payload, | ||
tablePaginationLimit: payload, | ||
tablePaginationOffset: 0, | ||
tablePaginationCurrentPage: 0, | ||
}; | ||
}, | ||
setTablePaginationCurrentPage(state: any, newPageNb: number) { | ||
// const updatedOffset = (newPageNb + 1) * state.tablePaginationLimit; | ||
const updatedOffset = newPageNb * state.tablePaginationLimit; | ||
return { ...state, tablePaginationCurrentPage: newPageNb, tablePaginationOffset: updatedOffset }; | ||
}, | ||
setTablePaginationOffset(state: any, payload: any) { | ||
return { ...state, tablePaginationOffset: payload }; | ||
}, | ||
setTablePaginationLimit(state: any, payload: any) { | ||
return { ...state, tablePaginationLimit: payload, tablePaginationCurrentPage: 0, tablePaginationOffset: 0 }; | ||
}, | ||
setQuery(state: any, payload: any) { | ||
return { ...state, query: payload }; | ||
}, | ||
setQueries(state: any, payload: any) { | ||
return { ...state, queries: payload }; | ||
}, | ||
}, | ||
effects: (dispatch: Dispatch) => ({ | ||
async initView() { | ||
const logger = log.noConflict(); | ||
if (process.env.NODE_ENV !== 'production') { | ||
logger.enableAll(); | ||
} else { | ||
logger.disableAll(); | ||
} | ||
logger.info('testingCases Logger initialized'); | ||
dispatch.testingCases.setLog(logger); | ||
}, | ||
|
||
async saveQuery() { | ||
console.log('MODEL SAVE QUERY'); | ||
}, | ||
async deleteQuery() { | ||
console.log('MODEL DELETE QUERY'); | ||
}, | ||
|
||
async updateQueryIfDifferent(newQuery: any, rootState: any) { | ||
const originalQuery = rootState.testingCases.query; | ||
// Only update the store if the query is different than store | ||
// Might need to replace stringify by Lodash isEqual | ||
if (JSON.stringify(originalQuery) !== JSON.stringify(newQuery)) { | ||
if (newQuery === null) { | ||
dispatch.testingCases.setQuery({}); | ||
} else { | ||
dispatch.testingCases.setQuery(newQuery); | ||
} | ||
} | ||
}, | ||
|
||
async updateTabIfDifferent(newTab: any, rootState: any) { | ||
const originalTab = rootState.testingCases.selectedTab; | ||
if (originalTab !== newTab) { | ||
dispatch.testingCases.setSelectedTab(newTab); | ||
} | ||
}, | ||
}), | ||
}; |
25 changes: 25 additions & 0 deletions
25
src/views/testingCases/content/explore/failureEvolution/getFailure.graphql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
query($query: String, $interval: String) { | ||
testingCases { | ||
data(query: $query) { | ||
failurerate(interval: $interval, buckets: 50) { | ||
field | ||
fromDateStart | ||
toDateStart | ||
buckets { | ||
key | ||
docCount | ||
caseFailureRate | ||
caseTotal | ||
caseTotalAvg | ||
buckets { | ||
dateStart | ||
docCount | ||
caseFailureRate | ||
caseTotal | ||
caseTotalAvg | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
175 changes: 175 additions & 0 deletions
175
src/views/testingCases/content/explore/failureEvolution/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
import React from 'react'; | ||
import { loader } from 'graphql.macro'; | ||
import { useQuery } from '@apollo/client'; | ||
import add from 'date-fns/add'; | ||
import Typography from '@material-ui/core/Typography'; | ||
import Grid from '@material-ui/core/Grid'; | ||
import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'; | ||
import AddBoxIcon from '@material-ui/icons/AddBox'; | ||
import IndeterminateCheckBoxIcon from '@material-ui/icons/IndeterminateCheckBox'; | ||
import { startOfWeek, startOfMonth } from 'date-fns'; | ||
|
||
import CustomCard from '../../../../../components/customCard'; | ||
import FailureChart from '../../../../../components/charts/nivo/failureChart'; | ||
|
||
const GQL_QUERY = loader('./getFailure.graphql'); | ||
|
||
interface Props { | ||
query: any; | ||
timeWindowPrior: string; | ||
interval: string; | ||
headerTitle: string; | ||
} | ||
|
||
const buildQuery = (sourceQuery: any, additionalData: any) => { | ||
let updatedQuery: any = {}; | ||
|
||
if (Object.keys(sourceQuery).length === 0) { | ||
updatedQuery = { | ||
op: 'and', | ||
content: [], | ||
}; | ||
} else { | ||
updatedQuery = { ...sourceQuery }; | ||
} | ||
|
||
return { | ||
...updatedQuery, | ||
content: [...updatedQuery.content, ...additionalData], | ||
}; | ||
}; | ||
|
||
const getEmptyWeekCalendar = (firstWeek: Date, lastWeek: Date) => { | ||
const weekCalendar: Array<string> = []; | ||
const currentDate = firstWeek; | ||
lastWeek = add(lastWeek, { days: 1 }); | ||
while (currentDate <= lastWeek) { | ||
const currentWeekU = startOfWeek(currentDate, { weekStartsOn: 1 }); | ||
currentWeekU.setUTCHours(0, 0, 0, 0); // Needed not to take local browser timezone in consideration. | ||
const currentWeek = currentWeekU.toISOString(); | ||
if (!weekCalendar.includes(currentWeek)) { | ||
weekCalendar.push(currentWeek); | ||
} | ||
currentDate.setDate(currentDate.getDate() + 1); | ||
} | ||
return weekCalendar; | ||
}; | ||
|
||
const getEmptyDayCalendar = (firstDay: Date, lastDay: Date) => { | ||
const dayCalendar: Array<string> = []; | ||
const currentDate = firstDay; | ||
lastDay = add(lastDay, { days: 1 }); | ||
while (currentDate <= lastDay) { | ||
currentDate.setUTCHours(0, 0, 0, 0); // Needed not to take local browser timezone in consideration. | ||
dayCalendar.push(currentDate.toISOString()); | ||
currentDate.setDate(currentDate.getDate() + 1); | ||
} | ||
return dayCalendar; | ||
}; | ||
|
||
const getEmptyMonthCalendar = (firstMonth: Date, lastMonth: Date) => { | ||
const weekCalendar: Array<string> = []; | ||
const currentDate = firstMonth; | ||
lastMonth = add(lastMonth, { days: 1 }); | ||
while (currentDate <= lastMonth) { | ||
const currentMonthU = startOfMonth(currentDate); | ||
currentMonthU.setUTCHours(0, 0, 0, 0); // Needed not to take local browser timezone in consideration. | ||
const currentMonth = currentMonthU.toISOString(); | ||
if (!weekCalendar.includes(currentMonth)) { | ||
weekCalendar.push(currentMonth); | ||
} | ||
currentDate.setDate(currentDate.getDate() + 1); | ||
} | ||
return weekCalendar; | ||
}; | ||
|
||
const buildDataset = (data: any, emptyCalendar: Array<string>) => { | ||
const dataset = []; | ||
for (const bucket of data.buckets.filter((b: any) => b.caseTotal > 0)) { | ||
const formattedBucket: any = {}; | ||
formattedBucket['serie'] = bucket.key; | ||
formattedBucket['avg'] = bucket.caseFailureRate; | ||
for (const week of emptyCalendar) { | ||
const dateExists = bucket.buckets.find((w: any) => w.dateStart === week); | ||
if (week !== 'avg') { | ||
if (dateExists === undefined || dateExists.docCount === 0) { | ||
formattedBucket[week] = -1; | ||
} else { | ||
formattedBucket[week] = dateExists.caseFailureRate; | ||
} | ||
} | ||
} | ||
dataset.push(formattedBucket); | ||
} | ||
return dataset; | ||
}; | ||
|
||
const FailureEvolution: React.FC<Props> = (props: Props) => { | ||
const { query, timeWindowPrior, headerTitle, interval } = props; | ||
|
||
const timeWindow = [{ op: '>=', content: { field: 'createdAt', value: timeWindowPrior } }]; | ||
|
||
const { data } = useQuery(GQL_QUERY, { | ||
variables: { | ||
query: JSON.stringify(buildQuery(query, timeWindow)), | ||
interval: interval, | ||
}, | ||
fetchPolicy: 'network-only', | ||
}); | ||
if (data !== undefined) { | ||
const dataset = data.testingCases.data.failurerate; | ||
let emptyCalendar: Array<string> = getEmptyWeekCalendar( | ||
new Date(dataset.fromDateStart), | ||
new Date(dataset.toDateStart), | ||
); | ||
if (interval === 'day') { | ||
emptyCalendar = getEmptyDayCalendar(new Date(dataset.fromDateStart), new Date(dataset.toDateStart)); | ||
} else if (interval === 'month') { | ||
emptyCalendar = getEmptyMonthCalendar(new Date(dataset.fromDateStart), new Date(dataset.toDateStart)); | ||
} | ||
|
||
emptyCalendar.unshift('avg'); | ||
|
||
const updatedDataset = buildDataset(dataset, emptyCalendar); | ||
return ( | ||
<CustomCard headerTitle={headerTitle}> | ||
<FailureChart dataset={updatedDataset} weeks={emptyCalendar} interval={interval} buckets={dataset.buckets} /> | ||
<Grid container spacing={1} direction="row" justify="center" alignItems="center"> | ||
<Grid item> | ||
<Typography variant="caption" display="block" gutterBottom> | ||
From period average: | ||
</Typography> | ||
</Grid> | ||
<Grid item> | ||
<CheckBoxOutlineBlankIcon /> | ||
<Typography variant="caption" display="block" gutterBottom> | ||
Less than 2% variance | ||
</Typography> | ||
</Grid> | ||
<Grid item> | ||
<IndeterminateCheckBoxIcon style={{ color: 'rgb(244, 117, 96)' }} /> | ||
<Typography variant="caption" display="block" gutterBottom> | ||
Degradation (> 2%) | ||
</Typography> | ||
</Grid> | ||
<Grid item> | ||
<AddBoxIcon style={{ color: 'rgb(97, 205, 187)' }} /> | ||
<Typography variant="caption" display="block" gutterBottom> | ||
Improvement (> 2%) | ||
</Typography> | ||
</Grid> | ||
</Grid> | ||
<Grid container spacing={1} direction="row" justify="center" alignItems="center"> | ||
<Grid item> | ||
<Typography variant="caption" display="block" gutterBottom> | ||
Displays the top 50 test cases with most failures, use facets on the left to filter down. | ||
</Typography> | ||
</Grid> | ||
</Grid> | ||
</CustomCard> | ||
); | ||
} | ||
return <span>Loading data</span>; | ||
}; | ||
|
||
export default FailureEvolution; |
Oops, something went wrong.