Skip to content

Commit e709de7

Browse files
committed
Leaderboard [percy]
1 parent 2e1b262 commit e709de7

File tree

9 files changed

+194
-25
lines changed

9 files changed

+194
-25
lines changed

app/components/leaderboard-page/entries-table.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
{{#if this.shouldShowSurroundingEntries}}
3030
<LeaderboardPage::EntriesTable::FillerRow />
3131

32-
{{#each this.sortedSurroundingEntries as |entry index|}}
32+
{{#each this.sortedSurroundingEntries as |entry|}}
3333
<LeaderboardPage::EntriesTable::Row @entry={{entry}} @rankText={{"#~5000"}} />
3434
{{/each}}
3535
{{/if}}

app/components/leaderboard-page/entries-table.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,6 @@ export default class LeaderboardPageEntriesTable extends Component<Signature> {
1919
@service declare authenticator: AuthenticatorService;
2020
@service declare store: Store;
2121

22-
get hasEntries() {
23-
return this.args.topEntries.length > 0;
24-
}
25-
26-
get explanationMarkdownForStagesCompleted() {
27-
return `There are ${this.args.language.stagesCount} stages available in this track.`;
28-
}
29-
3022
get explanationMarkdownForScore() {
3123
return `
3224
The highest possible score for this track is ${this.args.topEntries[0]!.leaderboard.highestPossibleScore}.
@@ -35,6 +27,14 @@ Harder stages have higher scores assigned to them.
3527
`.trim();
3628
}
3729

30+
get explanationMarkdownForStagesCompleted() {
31+
return `There are ${this.args.language.stagesCount} stages available in this track.`;
32+
}
33+
34+
get hasEntries() {
35+
return this.args.topEntries.length > 0;
36+
}
37+
3838
get shouldShowSurroundingEntries(): boolean {
3939
return !!(this.authenticator.isAuthenticated && !this.userIsInTopLeaderboardEntries && this.sortedSurroundingEntries.length > 0);
4040
}

app/components/leaderboard-page/entries-table/row.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<tr class="{{if this.isCurrentUser 'bg-teal-50 ring ring-teal-500 relative' 'hover:bg-gray-50'}} group/table-row">
1+
<tr class="{{if this.isCurrentUser 'bg-teal-50 ring ring-teal-500 relative' 'hover:bg-gray-50'}} group/table-row" data-test-leaderboard-entry-row>
22
<td
33
class="px-4 py-2 whitespace-nowrap text-xs font-medium
44
{{if this.isCurrentUser 'text-teal-600' 'text-gray-400'}}

app/components/leaderboard-page/entries-table/row.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ interface Signature {
99

1010
Args: {
1111
entry: LeaderboardEntryModel;
12-
rankText: number;
12+
rankText: string;
1313
};
1414
}
1515

@@ -18,17 +18,17 @@ const MAX_VISIBLE_COURSES = 7;
1818
export default class LeaderboardPageEntriesTableRow extends Component<Signature> {
1919
@service declare authenticator: AuthenticatorService;
2020

21+
get hiddenCourses(): CourseModel[] {
22+
return this.args.entry.relatedCourses.slice(0, -MAX_VISIBLE_COURSES);
23+
}
24+
2125
get isCurrentUser(): boolean {
2226
return this.args.entry.user === this.authenticator.currentUser;
2327
}
2428

2529
get visibleCourses(): CourseModel[] {
2630
return this.args.entry.relatedCourses.slice(-MAX_VISIBLE_COURSES);
2731
}
28-
29-
get hiddenCourses(): CourseModel[] {
30-
return this.args.entry.relatedCourses.slice(0, -MAX_VISIBLE_COURSES);
31-
}
3232
}
3333

3434
declare module '@glint/environment-ember-loose/registry' {

app/components/leaderboard-page/header.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
Leaderboard
77
</h3>
88
<Pill @color="green">
9-
BETA
9+
ALPHA
1010
<EmberTooltip @text="We're still working on this feature, rough edges expected!" @side="top" />
1111
</Pill>
1212
</div>

app/models/leaderboard-entry.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export default class LeaderboardEntryModel extends Model {
1919

2020
get relatedCourses(): CourseModel[] {
2121
const allCourses = this.store.peekAll('course');
22+
2223
return this.relatedCourseSlugs.map((slug) => allCourses.find((course) => course.slug === slug)).filter(Boolean);
2324
}
2425
}

app/routes/leaderboard.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,15 @@ export type ModelType = {
1616
};
1717

1818
export default class LeaderboardRoute extends BaseRoute {
19+
allowsAnonymousAccess = true;
20+
1921
@service declare authenticator: AuthenticatorService;
2022
@service declare store: Store;
2123

2224
activate(): void {
2325
scrollToTop();
2426
}
2527

26-
afterModel(_model: ModelType): void {
27-
if (!this.authenticator.currentUser?.isStaff) {
28-
this.router.transitionTo('not-found');
29-
30-
return;
31-
}
32-
}
33-
3428
async model(params: { language_slug: string }): Promise<ModelType> {
3529
(await this.store.findAll('course', {
3630
include: 'extensions,stages,language-configurations.language',
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import { currentURL } from '@ember/test-helpers';
2+
import testScenario from 'codecrafters-frontend/mirage/scenarios/test';
3+
import { setupApplicationTest } from 'codecrafters-frontend/tests/helpers';
4+
import leaderboardPage from 'codecrafters-frontend/tests/pages/leaderboard-page';
5+
import { signInAsStaff } from 'codecrafters-frontend/tests/support/authentication-helpers';
6+
import { setupAnimationTest } from 'ember-animated/test-support';
7+
import { setupWindowMock } from 'ember-window-mock/test-support';
8+
import { module, test } from 'qunit';
9+
import percySnapshot from '@percy/ember';
10+
11+
module('Acceptance | leaderboard-page | view', function (hooks) {
12+
setupApplicationTest(hooks);
13+
setupAnimationTest(hooks);
14+
setupWindowMock(hooks);
15+
16+
hooks.beforeEach(function () {
17+
testScenario(this.server);
18+
19+
for (const language of this.server.schema.languages.all().models) {
20+
language.update({ leaderboard: this.server.create('leaderboard', { highestPossibleScore: 237 }) });
21+
}
22+
});
23+
24+
test('can view', async function (assert) {
25+
const user9 = signInAsStaff(this.owner, this.server);
26+
27+
const language = this.server.schema.languages.all().models.find((language) => language.slug === 'rust');
28+
const leaderboard = language.leaderboard;
29+
30+
const user1 = this.server.create('user', {
31+
id: 'user-1',
32+
avatarUrl: 'https://github.com/Gufran.png',
33+
username: 'Gufran',
34+
});
35+
36+
const user2 = this.server.create('user', {
37+
id: 'user-2',
38+
avatarUrl: 'https://github.com/torvalds.png',
39+
username: 'torvalds',
40+
});
41+
42+
const user3 = this.server.create('user', {
43+
id: 'user-3',
44+
avatarUrl: 'https://github.com/addyosmani.png',
45+
username: 'addyosmaniveryverylongusername-with-lots-of-characters',
46+
});
47+
48+
const user4 = this.server.create('user', {
49+
id: 'user-4',
50+
avatarUrl: 'https://github.com/gaearon.png',
51+
username: 'gaearon',
52+
});
53+
54+
const user5 = this.server.create('user', {
55+
id: 'user-5',
56+
avatarUrl: 'https://github.com/yyx990803.png',
57+
username: 'yyx990803',
58+
});
59+
60+
const user6 = this.server.create('user', {
61+
id: 'user-6',
62+
avatarUrl: 'https://github.com/getify.png',
63+
username: 'getify',
64+
});
65+
66+
const user7 = this.server.create('user', {
67+
id: 'user-7',
68+
avatarUrl: 'https://github.com/rauchg.png',
69+
username: 'rauchg',
70+
});
71+
72+
const user8 = this.server.create('user', {
73+
id: 'user-8',
74+
avatarUrl: 'https://github.com/addyosmani.png',
75+
username: 'addyosmani',
76+
});
77+
78+
const user10 = this.server.create('user', {
79+
id: 'user-10',
80+
avatarUrl: 'https://github.com/defunkt.png',
81+
username: 'defunkt',
82+
});
83+
84+
this.server.create('leaderboard-entry', {
85+
leaderboard,
86+
user: user1,
87+
score: 96,
88+
scoreUpdatesCount: 7,
89+
relatedCourseSlugs: ['redis', 'shell', 'sqlite', 'grep'],
90+
});
91+
92+
this.server.create('leaderboard-entry', {
93+
leaderboard,
94+
user: user2,
95+
score: 221,
96+
scoreUpdatesCount: 19,
97+
relatedCourseSlugs: ['redis', 'sqlite', 'grep'],
98+
});
99+
100+
this.server.create('leaderboard-entry', {
101+
leaderboard,
102+
user: user3,
103+
score: 187,
104+
scoreUpdatesCount: 18,
105+
relatedCourseSlugs: ['redis', 'shell'],
106+
});
107+
108+
this.server.create('leaderboard-entry', {
109+
leaderboard,
110+
user: user4,
111+
score: 142,
112+
scoreUpdatesCount: 17,
113+
relatedCourseSlugs: ['redis', 'shell'],
114+
});
115+
116+
this.server.create('leaderboard-entry', {
117+
leaderboard,
118+
user: user5,
119+
score: 110,
120+
scoreUpdatesCount: 16,
121+
relatedCourseSlugs: ['redis', 'shell'],
122+
});
123+
124+
this.server.create('leaderboard-entry', {
125+
leaderboard,
126+
user: user6,
127+
score: 99,
128+
scoreUpdatesCount: 15,
129+
relatedCourseSlugs: ['redis', 'shell'],
130+
});
131+
132+
this.server.create('leaderboard-entry', {
133+
leaderboard,
134+
user: user7,
135+
score: 88,
136+
scoreUpdatesCount: 14,
137+
relatedCourseSlugs: ['redis', 'shell'],
138+
});
139+
140+
this.server.create('leaderboard-entry', {
141+
leaderboard,
142+
user: user8,
143+
score: 77,
144+
scoreUpdatesCount: 13,
145+
relatedCourseSlugs: ['redis', 'shell'],
146+
});
147+
148+
this.server.create('leaderboard-entry', {
149+
leaderboard,
150+
user: user9,
151+
score: 66,
152+
scoreUpdatesCount: 12,
153+
relatedCourseSlugs: ['redis', 'git'],
154+
});
155+
156+
this.server.create('leaderboard-entry', {
157+
leaderboard,
158+
user: user10,
159+
score: 55,
160+
scoreUpdatesCount: 4,
161+
relatedCourseSlugs: ['redis', 'shell'],
162+
});
163+
164+
await leaderboardPage.visit({ language_slug: 'rust' });
165+
assert.strictEqual(currentURL(), '/leaderboards/rust');
166+
167+
assert.strictEqual(leaderboardPage.entriesTable.entries.length, 10, '10 entries should be shown');
168+
await percySnapshot('Leaderboard Page');
169+
});
170+
});

tests/pages/leaderboard-page.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
import { text, visitable } from 'ember-cli-page-object';
1+
import { collection, text, visitable } from 'ember-cli-page-object';
22
import createPage from 'codecrafters-frontend/tests/support/create-page';
33

44
export default createPage({
55
description: text('[data-test-leaderboard-description]'),
66

7+
entriesTable: {
8+
entries: collection('[data-test-leaderboard-entry-row]'),
9+
},
10+
711
languageDropdown: {
812
scope: '[data-test-language-dropdown]',
913
},

0 commit comments

Comments
 (0)