{{template "repo/header" .}} -
-

- {{DateTime "long" .DateFrom}} - {{DateTime "long" .DateUntil}} - - -

-
- - {{if (or (.Permission.CanRead $.UnitTypeIssues) (.Permission.CanRead $.UnitTypePullRequests))}} -

{{ctx.Locale.Tr "repo.activity.overview"}}

-
- {{if .Permission.CanRead $.UnitTypePullRequests}} -
- {{if gt .Activity.ActivePRCount 0}} -
- - -
- {{else}} -
- -
- {{end}} - {{ctx.Locale.TrN .Activity.ActivePRCount "repo.activity.active_prs_count_1" "repo.activity.active_prs_count_n" .Activity.ActivePRCount | Safe}} -
- {{end}} - {{if .Permission.CanRead $.UnitTypeIssues}} -
- {{if gt .Activity.ActiveIssueCount 0}} -
- - -
- {{else}} -
- -
- {{end}} - {{ctx.Locale.TrN .Activity.ActiveIssueCount "repo.activity.active_issues_count_1" "repo.activity.active_issues_count_n" .Activity.ActiveIssueCount | Safe}} -
- {{end}} +
+
+ {{template "repo/navbar" .}}
-
- {{if .Permission.CanRead $.UnitTypePullRequests}} - - {{svg "octicon-git-pull-request"}} {{.Activity.MergedPRCount}}
- {{ctx.Locale.TrN .Activity.MergedPRCount "repo.activity.merged_prs_count_1" "repo.activity.merged_prs_count_n"}} -
- - {{svg "octicon-git-branch"}} {{.Activity.OpenedPRCount}}
- {{ctx.Locale.TrN .Activity.OpenedPRCount "repo.activity.opened_prs_count_1" "repo.activity.opened_prs_count_n"}} -
- {{end}} - {{if .Permission.CanRead $.UnitTypeIssues}} - - {{svg "octicon-issue-closed"}} {{.Activity.ClosedIssueCount}}
- {{ctx.Locale.TrN .Activity.ClosedIssueCount "repo.activity.closed_issues_count_1" "repo.activity.closed_issues_count_n"}} -
- - {{svg "octicon-issue-opened"}} {{.Activity.OpenedIssueCount}}
- {{ctx.Locale.TrN .Activity.OpenedIssueCount "repo.activity.new_issues_count_1" "repo.activity.new_issues_count_n"}} -
- {{end}} +
+ {{if .PageIsPulse}}{{template "repo/pulse" .}}{{end}} + {{if .PageIsContributors}}{{template "repo/contributors" .}}{{end}}
- {{end}} - - {{if .Permission.CanRead $.UnitTypeCode}} - {{if eq .Activity.Code.CommitCountInAllBranches 0}} -
-

{{ctx.Locale.Tr "repo.activity.no_git_activity"}}

-
- {{end}} - {{if gt .Activity.Code.CommitCountInAllBranches 0}} -
-
- {{ctx.Locale.Tr "repo.activity.git_stats_exclude_merges"}} - {{ctx.Locale.TrN .Activity.Code.AuthorCount "repo.activity.git_stats_author_1" "repo.activity.git_stats_author_n" .Activity.Code.AuthorCount}} - {{ctx.Locale.TrN .Activity.Code.AuthorCount "repo.activity.git_stats_pushed_1" "repo.activity.git_stats_pushed_n"}} - {{ctx.Locale.TrN .Activity.Code.CommitCount "repo.activity.git_stats_commit_1" "repo.activity.git_stats_commit_n" .Activity.Code.CommitCount}} - {{ctx.Locale.Tr "repo.activity.git_stats_push_to_branch" .Repository.DefaultBranch}} - {{ctx.Locale.TrN .Activity.Code.CommitCountInAllBranches "repo.activity.git_stats_commit_1" "repo.activity.git_stats_commit_n" .Activity.Code.CommitCountInAllBranches}} - {{ctx.Locale.Tr "repo.activity.git_stats_push_to_all_branches"}} - {{ctx.Locale.Tr "repo.activity.git_stats_on_default_branch" .Repository.DefaultBranch}} - {{ctx.Locale.TrN .Activity.Code.ChangedFiles "repo.activity.git_stats_file_1" "repo.activity.git_stats_file_n" .Activity.Code.ChangedFiles}} - {{ctx.Locale.TrN .Activity.Code.ChangedFiles "repo.activity.git_stats_files_changed_1" "repo.activity.git_stats_files_changed_n"}} - {{ctx.Locale.Tr "repo.activity.git_stats_additions"}} - {{ctx.Locale.TrN .Activity.Code.Additions "repo.activity.git_stats_addition_1" "repo.activity.git_stats_addition_n" .Activity.Code.Additions}} - {{ctx.Locale.Tr "repo.activity.git_stats_and_deletions"}} - {{ctx.Locale.TrN .Activity.Code.Deletions "repo.activity.git_stats_deletion_1" "repo.activity.git_stats_deletion_n" .Activity.Code.Deletions}}. -
-
-
-
-
- {{end}} - {{end}} - - {{if gt .Activity.PublishedReleaseCount 0}} -

- {{svg "octicon-tag" 16 "gt-mr-3"}} - {{ctx.Locale.Tr "repo.activity.title.releases_published_by" - (ctx.Locale.TrN .Activity.PublishedReleaseCount "repo.activity.title.releases_1" "repo.activity.title.releases_n" .Activity.PublishedReleaseCount) - (ctx.Locale.TrN .Activity.PublishedReleaseAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.PublishedReleaseAuthorCount) - }} -

-
- {{range .Activity.PublishedReleases}} -

- {{ctx.Locale.Tr "repo.activity.published_release_label"}} - {{.TagName}} - {{if not .IsTag}} - {{.Title | RenderEmoji $.Context | RenderCodeBlock}} - {{end}} - {{TimeSinceUnix .CreatedUnix ctx.Locale}} -

- {{end}} -
- {{end}} - - {{if gt .Activity.MergedPRCount 0}} -

- {{svg "octicon-git-pull-request" 16 "gt-mr-3"}} - {{ctx.Locale.Tr "repo.activity.title.prs_merged_by" - (ctx.Locale.TrN .Activity.MergedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.MergedPRCount) - (ctx.Locale.TrN .Activity.MergedPRAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.MergedPRAuthorCount) - }} -

-
- {{range .Activity.MergedPRs}} -

- {{ctx.Locale.Tr "repo.activity.merged_prs_label"}} - #{{.Index}} {{.Issue.Title | RenderEmoji $.Context | RenderCodeBlock}} - {{TimeSinceUnix .MergedUnix ctx.Locale}} -

- {{end}} -
- {{end}} - - {{if gt .Activity.OpenedPRCount 0}} -

- {{svg "octicon-git-branch" 16 "gt-mr-3"}} - {{ctx.Locale.Tr "repo.activity.title.prs_opened_by" - (ctx.Locale.TrN .Activity.OpenedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.OpenedPRCount) - (ctx.Locale.TrN .Activity.OpenedPRAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.OpenedPRAuthorCount) - }} -

-
- {{range .Activity.OpenedPRs}} -

- {{ctx.Locale.Tr "repo.activity.opened_prs_label"}} - #{{.Index}} {{.Issue.Title | RenderEmoji $.Context | RenderCodeBlock}} - {{TimeSinceUnix .Issue.CreatedUnix ctx.Locale}} -

- {{end}} -
- {{end}} - - {{if gt .Activity.ClosedIssueCount 0}} -

- {{svg "octicon-issue-closed" 16 "gt-mr-3"}} - {{ctx.Locale.Tr "repo.activity.title.issues_closed_from" - (ctx.Locale.TrN .Activity.ClosedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.ClosedIssueCount) - (ctx.Locale.TrN .Activity.ClosedIssueAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.ClosedIssueAuthorCount) - }} -

-
- {{range .Activity.ClosedIssues}} -

- {{ctx.Locale.Tr "repo.activity.closed_issue_label"}} - #{{.Index}} {{.Title | RenderEmoji $.Context | RenderCodeBlock}} - {{TimeSinceUnix .ClosedUnix ctx.Locale}} -

- {{end}} -
- {{end}} - - {{if gt .Activity.OpenedIssueCount 0}} -

- {{svg "octicon-issue-opened" 16 "gt-mr-3"}} - {{ctx.Locale.Tr "repo.activity.title.issues_created_by" - (ctx.Locale.TrN .Activity.OpenedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.OpenedIssueCount) - (ctx.Locale.TrN .Activity.OpenedIssueAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.OpenedIssueAuthorCount) - }} -

-
- {{range .Activity.OpenedIssues}} -

- {{ctx.Locale.Tr "repo.activity.new_issue_label"}} - #{{.Index}} {{.Title | RenderEmoji $.Context | RenderCodeBlock}} - {{TimeSinceUnix .CreatedUnix ctx.Locale}} -

- {{end}} -
- {{end}} - - {{if gt .Activity.UnresolvedIssueCount 0}} -

- {{svg "octicon-comment-discussion" 16 "gt-mr-3"}} - {{ctx.Locale.TrN .Activity.UnresolvedIssueCount "repo.activity.title.unresolved_conv_1" "repo.activity.title.unresolved_conv_n" .Activity.UnresolvedIssueCount}} -

-
- {{range .Activity.UnresolvedIssues}} -

- {{ctx.Locale.Tr "repo.activity.unresolved_conv_label"}} - #{{.Index}} - {{if .IsPull}} - {{.Title | RenderEmoji $.Context | RenderCodeBlock}} - {{else}} - {{.Title | RenderEmoji $.Context | RenderCodeBlock}} - {{end}} - {{TimeSinceUnix .UpdatedUnix ctx.Locale}} -

- {{end}} -
- {{end}}
{{template "base/footer" .}} + diff --git a/templates/repo/contributors.tmpl b/templates/repo/contributors.tmpl new file mode 100644 index 0000000000..49a251c1f9 --- /dev/null +++ b/templates/repo/contributors.tmpl @@ -0,0 +1,13 @@ +{{if .Permission.CanRead $.UnitTypeCode}} +
+
+{{end}} diff --git a/templates/repo/navbar.tmpl b/templates/repo/navbar.tmpl new file mode 100644 index 0000000000..a9042ee30d --- /dev/null +++ b/templates/repo/navbar.tmpl @@ -0,0 +1,8 @@ + diff --git a/templates/repo/pulse.tmpl b/templates/repo/pulse.tmpl new file mode 100644 index 0000000000..ccd7ebf6b5 --- /dev/null +++ b/templates/repo/pulse.tmpl @@ -0,0 +1,227 @@ +

+ {{DateTime "long" .DateFrom}} - {{DateTime "long" .DateUntil}} + + +

+ +{{if (or (.Permission.CanRead $.UnitTypeIssues) (.Permission.CanRead $.UnitTypePullRequests))}} +

{{ctx.Locale.Tr "repo.activity.overview"}}

+
+ {{if .Permission.CanRead $.UnitTypePullRequests}} +
+ {{if gt .Activity.ActivePRCount 0}} +
+ + +
+ {{else}} +
+ +
+ {{end}} + {{ctx.Locale.TrN .Activity.ActivePRCount "repo.activity.active_prs_count_1" "repo.activity.active_prs_count_n" .Activity.ActivePRCount | Safe}} +
+ {{end}} + {{if .Permission.CanRead $.UnitTypeIssues}} +
+ {{if gt .Activity.ActiveIssueCount 0}} +
+ + +
+ {{else}} +
+ +
+ {{end}} + {{ctx.Locale.TrN .Activity.ActiveIssueCount "repo.activity.active_issues_count_1" "repo.activity.active_issues_count_n" .Activity.ActiveIssueCount | Safe}} +
+ {{end}} +
+ +{{end}} + +{{if .Permission.CanRead $.UnitTypeCode}} + {{if eq .Activity.Code.CommitCountInAllBranches 0}} +
+

{{ctx.Locale.Tr "repo.activity.no_git_activity"}}

+
+ {{end}} + {{if gt .Activity.Code.CommitCountInAllBranches 0}} +
+
+ {{ctx.Locale.Tr "repo.activity.git_stats_exclude_merges"}} + {{ctx.Locale.TrN .Activity.Code.AuthorCount "repo.activity.git_stats_author_1" "repo.activity.git_stats_author_n" .Activity.Code.AuthorCount}} + {{ctx.Locale.TrN .Activity.Code.AuthorCount "repo.activity.git_stats_pushed_1" "repo.activity.git_stats_pushed_n"}} + {{ctx.Locale.TrN .Activity.Code.CommitCount "repo.activity.git_stats_commit_1" "repo.activity.git_stats_commit_n" .Activity.Code.CommitCount}} + {{ctx.Locale.Tr "repo.activity.git_stats_push_to_branch" .Repository.DefaultBranch}} + {{ctx.Locale.TrN .Activity.Code.CommitCountInAllBranches "repo.activity.git_stats_commit_1" "repo.activity.git_stats_commit_n" .Activity.Code.CommitCountInAllBranches}} + {{ctx.Locale.Tr "repo.activity.git_stats_push_to_all_branches"}} + {{ctx.Locale.Tr "repo.activity.git_stats_on_default_branch" .Repository.DefaultBranch}} + {{ctx.Locale.TrN .Activity.Code.ChangedFiles "repo.activity.git_stats_file_1" "repo.activity.git_stats_file_n" .Activity.Code.ChangedFiles}} + {{ctx.Locale.TrN .Activity.Code.ChangedFiles "repo.activity.git_stats_files_changed_1" "repo.activity.git_stats_files_changed_n"}} + {{ctx.Locale.Tr "repo.activity.git_stats_additions"}} + {{ctx.Locale.TrN .Activity.Code.Additions "repo.activity.git_stats_addition_1" "repo.activity.git_stats_addition_n" .Activity.Code.Additions}} + {{ctx.Locale.Tr "repo.activity.git_stats_and_deletions"}} + {{ctx.Locale.TrN .Activity.Code.Deletions "repo.activity.git_stats_deletion_1" "repo.activity.git_stats_deletion_n" .Activity.Code.Deletions}}. +
+
+
+
+
+ {{end}} +{{end}} + +{{if gt .Activity.PublishedReleaseCount 0}} +

+ {{svg "octicon-tag" 16 "gt-mr-3"}} + {{ctx.Locale.Tr "repo.activity.title.releases_published_by" + (ctx.Locale.TrN .Activity.PublishedReleaseCount "repo.activity.title.releases_1" "repo.activity.title.releases_n" .Activity.PublishedReleaseCount) + (ctx.Locale.TrN .Activity.PublishedReleaseAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.PublishedReleaseAuthorCount) + }} +

+
+ {{range .Activity.PublishedReleases}} +

+ {{ctx.Locale.Tr "repo.activity.published_release_label"}} + {{.TagName}} + {{if not .IsTag}} + {{.Title | RenderEmoji $.Context | RenderCodeBlock}} + {{end}} + {{TimeSinceUnix .CreatedUnix ctx.Locale}} +

+ {{end}} +
+{{end}} + +{{if gt .Activity.MergedPRCount 0}} +

+ {{svg "octicon-git-pull-request" 16 "gt-mr-3"}} + {{ctx.Locale.Tr "repo.activity.title.prs_merged_by" + (ctx.Locale.TrN .Activity.MergedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.MergedPRCount) + (ctx.Locale.TrN .Activity.MergedPRAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.MergedPRAuthorCount) + }} +

+
+ {{range .Activity.MergedPRs}} +

+ {{ctx.Locale.Tr "repo.activity.merged_prs_label"}} + #{{.Index}} {{.Issue.Title | RenderEmoji $.Context | RenderCodeBlock}} + {{TimeSinceUnix .MergedUnix ctx.Locale}} +

+ {{end}} +
+{{end}} + +{{if gt .Activity.OpenedPRCount 0}} +

+ {{svg "octicon-git-branch" 16 "gt-mr-3"}} + {{ctx.Locale.Tr "repo.activity.title.prs_opened_by" + (ctx.Locale.TrN .Activity.OpenedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.OpenedPRCount) + (ctx.Locale.TrN .Activity.OpenedPRAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.OpenedPRAuthorCount) + }} +

+
+ {{range .Activity.OpenedPRs}} +

+ {{ctx.Locale.Tr "repo.activity.opened_prs_label"}} + #{{.Index}} {{.Issue.Title | RenderEmoji $.Context | RenderCodeBlock}} + {{TimeSinceUnix .Issue.CreatedUnix ctx.Locale}} +

+ {{end}} +
+{{end}} + +{{if gt .Activity.ClosedIssueCount 0}} +

+ {{svg "octicon-issue-closed" 16 "gt-mr-3"}} + {{ctx.Locale.Tr "repo.activity.title.issues_closed_from" + (ctx.Locale.TrN .Activity.ClosedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.ClosedIssueCount) + (ctx.Locale.TrN .Activity.ClosedIssueAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.ClosedIssueAuthorCount) + }} +

+
+ {{range .Activity.ClosedIssues}} +

+ {{ctx.Locale.Tr "repo.activity.closed_issue_label"}} + #{{.Index}} {{.Title | RenderEmoji $.Context | RenderCodeBlock}} + {{TimeSinceUnix .ClosedUnix ctx.Locale}} +

+ {{end}} +
+{{end}} + +{{if gt .Activity.OpenedIssueCount 0}} +

+ {{svg "octicon-issue-opened" 16 "gt-mr-3"}} + {{ctx.Locale.Tr "repo.activity.title.issues_created_by" + (ctx.Locale.TrN .Activity.OpenedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.OpenedIssueCount) + (ctx.Locale.TrN .Activity.OpenedIssueAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.OpenedIssueAuthorCount) + }} +

+
+ {{range .Activity.OpenedIssues}} +

+ {{ctx.Locale.Tr "repo.activity.new_issue_label"}} + #{{.Index}} {{.Title | RenderEmoji $.Context | RenderCodeBlock}} + {{TimeSinceUnix .CreatedUnix ctx.Locale}} +

+ {{end}} +
+{{end}} + +{{if gt .Activity.UnresolvedIssueCount 0}} +

+ {{svg "octicon-comment-discussion" 16 "gt-mr-3"}} + {{ctx.Locale.TrN .Activity.UnresolvedIssueCount "repo.activity.title.unresolved_conv_1" "repo.activity.title.unresolved_conv_n" .Activity.UnresolvedIssueCount}} +

+
+ {{range .Activity.UnresolvedIssues}} +

+ {{ctx.Locale.Tr "repo.activity.unresolved_conv_label"}} + #{{.Index}} + {{if .IsPull}} + {{.Title | RenderEmoji $.Context | RenderCodeBlock}} + {{else}} + {{.Title | RenderEmoji $.Context | RenderCodeBlock}} + {{end}} + {{TimeSinceUnix .UpdatedUnix ctx.Locale}} +

+ {{end}} +
+{{end}} diff --git a/web_src/js/components/.eslintrc.yaml b/web_src/js/components/.eslintrc.yaml index 0cab470f6b..0d233442bc 100644 --- a/web_src/js/components/.eslintrc.yaml +++ b/web_src/js/components/.eslintrc.yaml @@ -7,6 +7,10 @@ extends: - plugin:vue/vue3-recommended - plugin:vue-scoped-css/vue3-recommended +parserOptions: + sourceType: module + ecmaVersion: latest + env: browser: true diff --git a/web_src/js/components/RepoContributors.vue b/web_src/js/components/RepoContributors.vue new file mode 100644 index 0000000000..fa1545b3df --- /dev/null +++ b/web_src/js/components/RepoContributors.vue @@ -0,0 +1,443 @@ + + + diff --git a/web_src/js/features/contributors.js b/web_src/js/features/contributors.js new file mode 100644 index 0000000000..66185ac315 --- /dev/null +++ b/web_src/js/features/contributors.js @@ -0,0 +1,28 @@ +import {createApp} from 'vue'; + +export async function initRepoContributors() { + const el = document.getElementById('repo-contributors-chart'); + if (!el) return; + + const {default: RepoContributors} = await import(/* webpackChunkName: "contributors-graph" */'../components/RepoContributors.vue'); + try { + const View = createApp(RepoContributors, { + locale: { + filterLabel: el.getAttribute('data-locale-filter-label'), + contributionType: { + commits: el.getAttribute('data-locale-contribution-type-commits'), + additions: el.getAttribute('data-locale-contribution-type-additions'), + deletions: el.getAttribute('data-locale-contribution-type-deletions'), + }, + + loadingTitle: el.getAttribute('data-locale-loading-title'), + loadingTitleFailed: el.getAttribute('data-locale-loading-title-failed'), + loadingInfo: el.getAttribute('data-locale-loading-info'), + } + }); + View.mount(el); + } catch (err) { + console.error('RepoContributors failed to load', err); + el.textContent = el.getAttribute('data-locale-component-failed-to-load'); + } +} diff --git a/web_src/js/index.js b/web_src/js/index.js index 4713618506..078f9fc9df 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -83,6 +83,7 @@ import {initGiteaFomantic} from './modules/fomantic.js'; import {onDomReady} from './utils/dom.js'; import {initRepoIssueList} from './features/repo-issue-list.js'; import {initCommonIssueListQuickGoto} from './features/common-issue-list.js'; +import {initRepoContributors} from './features/contributors.js'; import {initRepoDiffCommitBranchesAndTags} from './features/repo-diff-commit.js'; import {initDirAuto} from './modules/dirauto.js'; @@ -172,6 +173,7 @@ onDomReady(() => { initRepoWikiForm(); initRepository(); initRepositoryActionView(); + initRepoContributors(); initCommitStatuses(); initCaptcha(); diff --git a/web_src/js/utils/time.js b/web_src/js/utils/time.js new file mode 100644 index 0000000000..3284e893e1 --- /dev/null +++ b/web_src/js/utils/time.js @@ -0,0 +1,46 @@ +import dayjs from 'dayjs'; + +// Returns an array of millisecond-timestamps of start-of-week days (Sundays) +export function startDaysBetween(startDate, endDate) { + // Ensure the start date is a Sunday + while (startDate.getDay() !== 0) { + startDate.setDate(startDate.getDate() + 1); + } + + const start = dayjs(startDate); + const end = dayjs(endDate); + const startDays = []; + + let current = start; + while (current.isBefore(end)) { + startDays.push(current.valueOf()); + // we are adding 7 * 24 hours instead of 1 week because we don't want + // date library to use local time zone to calculate 1 week from now. + // local time zone is problematic because of daylight saving time (dst) + // used on some countries + current = current.add(7 * 24, 'hour'); + } + + return startDays; +} + +export function firstStartDateAfterDate(inputDate) { + if (!(inputDate instanceof Date)) { + throw new Error('Invalid date'); + } + const dayOfWeek = inputDate.getDay(); + const daysUntilSunday = 7 - dayOfWeek; + const resultDate = new Date(inputDate.getTime()); + resultDate.setDate(resultDate.getDate() + daysUntilSunday); + return resultDate.valueOf(); +} + +export function fillEmptyStartDaysWithZeroes(startDays, data) { + const result = {}; + + for (const startDay of startDays) { + result[startDay] = data[startDay] || {'week': startDay, 'additions': 0, 'deletions': 0, 'commits': 0}; + } + + return Object.values(result); +} diff --git a/web_src/js/utils/time.test.js b/web_src/js/utils/time.test.js new file mode 100644 index 0000000000..dd1114ce7f --- /dev/null +++ b/web_src/js/utils/time.test.js @@ -0,0 +1,15 @@ +import {startDaysBetween} from './time.js'; + +test('startDaysBetween', () => { + expect(startDaysBetween(new Date('2024-02-15'), new Date('2024-04-18'))).toEqual([ + 1708214400000, + 1708819200000, + 1709424000000, + 1710028800000, + 1710633600000, + 1711238400000, + 1711843200000, + 1712448000000, + 1713052800000, + ]); +});