Skip to content

Commit

Permalink
Feature: Refactored the build table interactor
Browse files Browse the repository at this point in the history
  • Loading branch information
manuelmhtr committed Dec 2, 2024
1 parent 5e4757d commit 0f373bb
Show file tree
Hide file tree
Showing 16 changed files with 579 additions and 342 deletions.
19 changes: 10 additions & 9 deletions src/interactors/buildTable/__tests__/calculateBests.test.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
const stats = require('../../__tests__/mocks/stats.json');
const { entries } = require('../../../../tests/mocks');
const calculateBests = require('../calculateBests');

describe('Interactors | .buildTable | .calculateBests', () => {
it('returns the best stats for MAX optimization', () => {
const response = calculateBests(stats);
it('returns the best stats for DESC sort order', () => {
const response = calculateBests(entries);
expect(response).toMatchObject({
totalReviews: 37,
totalComments: 99,
commentsPerReview: 3,
totalReviews: 4,
totalComments: 5,
commentsPerReview: 5,
openedPullRequests: 30,
});
});

it('returns the best stats for MIN optimization', () => {
const response = calculateBests(stats);
it('returns the best stats for ASC sort order', () => {
const response = calculateBests(entries);
expect(response).toMatchObject({
timeToReview: 25000,
timeToReview: 1_000_000,
});
});
});
218 changes: 115 additions & 103 deletions src/interactors/buildTable/__tests__/getTableData.test.js
Original file line number Diff line number Diff line change
@@ -1,127 +1,139 @@
const { t } = require('../../../i18n');
const reviewers = require('../../__tests__/mocks/populatedReviewers.json');
const { entries } = require('../../../../tests/mocks');
const { STATS, VALID_STATS } = require('../../../config/stats');

Check failure on line 3 in src/interactors/buildTable/__tests__/getTableData.test.js

View workflow job for this annotation

GitHub Actions / lint

Unable to resolve path to module '../../../config/stats'

Check failure on line 3 in src/interactors/buildTable/__tests__/getTableData.test.js

View workflow job for this annotation

GitHub Actions / lint

Missing file extension for "../../../config/stats"
const getTableData = require('../getTableData');

const bests = {
totalReviews: 4,
totalComments: 5,
commentsPerReview: 5,
timeToReview: 2052500,
openedPullRequests: 30,
timeToReview: 1_000_000,
};

const TITLES = {
avatar: t('table.columns.avatar'),
username: t('table.columns.username'),
timeToReview: t('table.columns.timeToReview'),
totalReviews: t('table.columns.totalReviews'),
totalComments: t('table.columns.totalComments'),
};
describe('Interactors | .buildTable | .getTableData', () => {
const defaultParams = {
bests,
entries,
mainStats: VALID_STATS,
disableLinks: true,
displayCharts: false,
};

const AVATAR1 = '<a href="https://github.com/user1"><img src="https://avatars.githubusercontent.com/u/1234" width="20"></a>';
const AVATAR2 = '<a href="https://github.com/user2"><img src="https://avatars.githubusercontent.com/u/5678" width="20"></a>';
const AVATAR1_BIG = '<a href="https://github.com/user1"><img src="https://avatars.githubusercontent.com/u/1234" width="32"></a>';
const AVATAR2_BIG = '<a href="https://github.com/user2"><img src="https://avatars.githubusercontent.com/u/5678" width="32"></a>';

const SIMPLE_RESPONSE = [
TITLES,
{
avatar: AVATAR1,
username: 'user1',
timeToReview: '[34m](https://app.flowwer.dev/charts/review-time/1)',
totalReviews: '4',
totalComments: '1',
},
{
avatar: AVATAR2,
username: 'user2',
timeToReview: '[2h 21m](https://app.flowwer.dev/charts/review-time/2)',
totalReviews: '1',
totalComments: '5',
},
];

const CHARTS_RESPONSE = [
TITLES,
{
avatar: AVATAR1_BIG,
username: 'user1<br/>🥇',
timeToReview: '[**34m**](https://app.flowwer.dev/charts/review-time/1)<br/>▀▀',
totalReviews: '**4**<br/>▀▀▀▀▀▀▀▀',
totalComments: '1<br/>▀▀',
},
{
avatar: AVATAR2_BIG,
username: 'user2<br/>🥈',
timeToReview: '[2h 21m](https://app.flowwer.dev/charts/review-time/2)<br/>▀▀▀▀▀▀▀▀',
totalReviews: '1<br/>▀▀',
totalComments: '**5**<br/>▀▀▀▀▀▀▀▀',
},
];

const NO_LINKS_RESPONSE = [
TITLES,
{
avatar: AVATAR1,
username: 'user1',
timeToReview: '34m',
totalReviews: '4',
totalComments: '1',
},
{
avatar: AVATAR2,
username: 'user2',
timeToReview: '2h 21m',
totalReviews: '1',
totalComments: '5',
},
];

const CHARTS_NO_LINKS_RESPONSE = [
TITLES,
{
avatar: AVATAR1_BIG,
username: 'user1<br/>🥇',
timeToReview: '**34m**<br/>▀▀',
totalReviews: '**4**<br/>▀▀▀▀▀▀▀▀',
totalComments: '1<br/>▀▀',
},
{
avatar: AVATAR2_BIG,
username: 'user2<br/>🥈',
timeToReview: '2h 21m<br/>▀▀▀▀▀▀▀▀',
totalReviews: '1<br/>▀▀',
totalComments: '**5**<br/>▀▀▀▀▀▀▀▀',
},
];
it('builds the headers successfully', () => {
const response = getTableData(defaultParams);
expect(response.headers[0]).toEqual({ text: t('table.columns.username') });
VALID_STATS.forEach((statName, index) => {
expect(response.headers[index + 1]).toEqual({ text: t(`table.columns.${statName}`) });
});
});

describe('Interactors | .buildTable | .getTableData', () => {
describe('when sending reviewers only', () => {
it('returns the default case data', () => {
const response = getTableData({ reviewers });
expect(response).toEqual(SIMPLE_RESPONSE);
it('builds the users correctly', () => {
const response = getTableData(defaultParams);
response.rows.forEach((row, index) => {
expect(row.user).toEqual({
link: entries[index].user.url,
image: entries[index].user.avatarUrl,
text: entries[index].user.login,
emoji: null,
});
});
});

describe('when sending bests and display charts', () => {
it('returns the data with charts and medals', () => {
const response = getTableData({ bests, reviewers, displayCharts: true });
expect(response).toEqual(CHARTS_RESPONSE);
it('builds the stats successfully', () => {
const response = getTableData(defaultParams);
response.rows.forEach((row) => {
row.stats.forEach((stat) => {
expect(stat).toEqual({
text: expect.any(String),
bold: expect.any(Boolean),
link: null,
chartValue: null,
});
});
});
});

describe('when disabling links', () => {
it('returns the data without external links', () => {
const response = getTableData({ reviewers, disableLinks: true });
expect(response).toEqual(NO_LINKS_RESPONSE);
it('displays only the requested stats', () => {
const testCases = [
VALID_STATS,
[VALID_STATS[0]],
[VALID_STATS[VALID_STATS.length - 1], VALID_STATS[0]],
];

testCases.forEach((mainStats) => {
const response = getTableData({ ...defaultParams, mainStats });
expect(response.headers.length).toEqual(mainStats.length + 1);
response.rows.forEach((row) => {
expect(row.stats.length).toEqual(mainStats.length);
});
});
});

describe('when disabling links but adding charts', () => {
it('returns the data without external links', () => {
const response = getTableData({
bests, reviewers, displayCharts: true, disableLinks: true,
it('adds the stats link when required', () => {
const response = getTableData({
...defaultParams,
disableLinks: false,
});

response.rows.forEach((row, index) => {
VALID_STATS.forEach((statName, statIndex) => {
const link = entries[index].urls[statName] || null;
expect(row.stats[statIndex].link).toEqual(link);
});
expect(response).toEqual(CHARTS_NO_LINKS_RESPONSE);
});
});

it('adds the bold option to the best stats', () => {
const mainStats = [STATS.totalReviews.id];
const response = getTableData({
...defaultParams,
mainStats,
});

expect(response.rows[0].stats[0].bold).toEqual(true);
expect(response.rows[1].stats[0].bold).toEqual(false);
expect(response.rows[2].stats[0].bold).toEqual(false);
});

it('adds the chart value when required', () => {
const mainStats = [STATS.totalReviews.id];
const response = getTableData({
...defaultParams,
mainStats,
displayCharts: true,
});

response.rows.forEach((row, index) => {
mainStats.forEach((statName, statIndex) => {
const chartValue = entries[index].contributions[statName];
expect(row.stats[statIndex].chartValue).toEqual(chartValue);
});
});
});

it('adds the emoji when required', () => {
const displayCharts = true;
const response = getTableData({
...defaultParams,
displayCharts,
});

expect(response.rows[0].user.emoji).toEqual('medal1');
expect(response.rows[1].user.emoji).toEqual('medal2');
expect(response.rows[2].user.emoji).toEqual('medal3');
});

it('parses the stat text correctly', () => {
const mainStats = [STATS.timeToReview.id];
const response = getTableData({
...defaultParams,
mainStats,
});
response.rows.forEach((row, index) => {
const value = entries[index].stats.timeToReview;
const text = STATS.timeToReview.parser(value);
expect(row.stats[0].text).toEqual(text);
});
});
});
70 changes: 38 additions & 32 deletions src/interactors/buildTable/__tests__/index.test.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,50 @@
const reviewers = require('../../__tests__/mocks/populatedReviewers.json');
const { entries } = require('../../../../tests/mocks');
const { VALID_STATS } = require('../../../config/stats');

Check failure on line 2 in src/interactors/buildTable/__tests__/index.test.js

View workflow job for this annotation

GitHub Actions / lint

Unable to resolve path to module '../../../config/stats'

Check failure on line 2 in src/interactors/buildTable/__tests__/index.test.js

View workflow job for this annotation

GitHub Actions / lint

Missing file extension for "../../../config/stats"
const getTableData = require('../getTableData');
const buildTable = require('../index');

const SIMPLE_RESPONSE = `| | User | Total reviews | Time to review | Total comments |
| ---------------------------------------------------------------------------------------------------------- | ----- | ------------- | -------------- | -------------- |
| <a href="https://github.com/user1"><img src="https://avatars.githubusercontent.com/u/1234" width="20"></a> | user1 | **4** | **34m** | 1 |
| <a href="https://github.com/user2"><img src="https://avatars.githubusercontent.com/u/5678" width="20"></a> | user2 | 1 | 2h 21m | **5** |`;

const CHARTS_RESPONSE = `| | User | Total reviews | Time to review | Total comments |
| ---------------------------------------------------------------------------------------------------------- | ------------ | ------------------ | ------------------- | ------------------ |
| <a href="https://github.com/user1"><img src="https://avatars.githubusercontent.com/u/1234" width="32"></a> | user1<br/>🥇 | **4**<br/>▀▀▀▀▀▀▀▀ | **34m**<br/>▀▀ | 1<br/>▀▀ |
| <a href="https://github.com/user2"><img src="https://avatars.githubusercontent.com/u/5678" width="32"></a> | user2<br/>🥈 | 1<br/>▀▀ | 2h 21m<br/>▀▀▀▀▀▀▀▀ | **5**<br/>▀▀▀▀▀▀▀▀ |`;

const LINKS_RESPONSE = `| | User | Total reviews | Time to review | Total comments |
| ---------------------------------------------------------------------------------------------------------- | ----- | ------------- | ------------------------------------------------------- | -------------- |
| <a href="https://github.com/user1"><img src="https://avatars.githubusercontent.com/u/1234" width="20"></a> | user1 | **4** | [**34m**](https://app.flowwer.dev/charts/review-time/1) | 1 |
| <a href="https://github.com/user2"><img src="https://avatars.githubusercontent.com/u/5678" width="20"></a> | user2 | 1 | [2h 21m](https://app.flowwer.dev/charts/review-time/2) | **5** |`;

const LINKS_AND_CHARTS_RESPONSE = `| | User | Total reviews | Time to review | Total comments |
| ---------------------------------------------------------------------------------------------------------- | ------------ | ------------------ | ------------------------------------------------------------------- | ------------------ |
| <a href="https://github.com/user1"><img src="https://avatars.githubusercontent.com/u/1234" width="32"></a> | user1<br/>🥇 | **4**<br/>▀▀▀▀▀▀▀▀ | [**34m**](https://app.flowwer.dev/charts/review-time/1)<br/>▀▀ | 1<br/>▀▀ |
| <a href="https://github.com/user2"><img src="https://avatars.githubusercontent.com/u/5678" width="32"></a> | user2<br/>🥈 | 1<br/>▀▀ | [2h 21m](https://app.flowwer.dev/charts/review-time/2)<br/>▀▀▀▀▀▀▀▀ | **5**<br/>▀▀▀▀▀▀▀▀ |`;
jest.mock('../getTableData', () => jest.fn());

describe('Interactors | .buildTable', () => {
it('returns all available reviewers in a set of pull requests', () => {
const response = buildTable({ reviewers, disableLinks: true });
expect(response).toEqual(SIMPLE_RESPONSE);
const defaultParams = {
entries,
limit: null,
sortKey: VALID_STATS[0],
mainStats: VALID_STATS,
disableLinks: true,
displayCharts: false,
};

getTableData.mockImplementation(jest.requireActual('../getTableData'));

beforeEach(() => {
getTableData.mockClear();
});

it('can add charts to the table', () => {
const response = buildTable({ reviewers, displayCharts: true, disableLinks: true });
expect(response).toEqual(CHARTS_RESPONSE);
it('limits the results', () => {
const response1 = buildTable(defaultParams);
expect(response1.rows.length).toEqual(entries.length);

const limit = 1;
const response2 = buildTable({ ...defaultParams, limit });
expect(response2.rows.length).toEqual(limit);
});

it('with links enabled by default', () => {
const response = buildTable({ reviewers });
expect(response).toEqual(LINKS_RESPONSE);
it('sorts data by the given key', () => {
const response = buildTable(defaultParams);
expect(response.rows[0].user.text).toEqual('user1');
expect(response.rows[1].user.text).toEqual('user2');
expect(response.rows[2].user.text).toEqual('user3');
});

it('with links and charts', () => {
const response = buildTable({ reviewers, displayCharts: true });
expect(response).toEqual(LINKS_AND_CHARTS_RESPONSE);
it('calls build table data with the correct params', () => {
buildTable(defaultParams);
expect(getTableData).toHaveBeenCalledWith(expect.objectContaining({
entries,
bests: expect.any(Object),
mainStats: defaultParams.mainStats,
disableLinks: defaultParams.disableLinks,
displayCharts: defaultParams.displayCharts,
}));
});
});
Loading

0 comments on commit 0f373bb

Please sign in to comment.