Skip to content

Commit bdf3725

Browse files
authored
Merge pull request #2745 from codecrafters-io/CC-1654
Update frontend to use `Course#visibility` attribute to selectively render courses
2 parents eb9258d + eb3c408 commit bdf3725

10 files changed

Lines changed: 108 additions & 8 deletions

File tree

‎app/components/user-page/course-progress-list.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ export default class CourseProgressListComponent extends Component<Signature> {
1515
get courseParticipationGroups() {
1616
const participationsGroupedByCourse: CourseParticipationModel[][] = Object.values(
1717
groupBy(
18-
this.args.user.courseParticipations.filter((participation) => !participation.course.releaseStatusIsDeprecated),
18+
this.args.user.courseParticipations.filter(
19+
(participation) => !participation.course.releaseStatusIsDeprecated && !participation.course.visibilityIsPrivate,
20+
),
1921
'course',
2022
),
2123
);

‎app/controllers/catalog.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export default class CatalogController extends Controller {
104104
this.authenticator.currentUser && (this.authenticator.currentUser.isStaff || this.authenticator.currentUser.isCourseAuthor(course));
105105
const userHasRepository = this.authenticator.currentUser && this.authenticator.currentUser.repositories.filterBy('course', course).length > 0;
106106

107-
if (course.releaseStatusIsDeprecated) {
107+
if (course.releaseStatusIsDeprecated || course.visibilityIsPrivate) {
108108
return userHasRepository;
109109
}
110110

‎app/controllers/join-track.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export default class JoinTrackController extends Controller {
1212
@service declare authenticator: AuthenticatorService;
1313

1414
get courses(): CourseModel[] {
15-
return this.model.courses.rejectBy('releaseStatusIsAlpha').rejectBy('releaseStatusIsDeprecated');
15+
return this.model.courses.rejectBy('releaseStatusIsAlpha').rejectBy('releaseStatusIsDeprecated').rejectBy('visibilityIsPrivate');
1616
}
1717

1818
get sortedCourses(): CourseModel[] {

‎app/controllers/track.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export default class TrackController extends Controller {
1414
return this.model.courses;
1515
}
1616

17-
return this.model.courses.rejectBy('releaseStatusIsAlpha').rejectBy('releaseStatusIsDeprecated');
17+
return this.model.courses.rejectBy('releaseStatusIsAlpha').rejectBy('releaseStatusIsDeprecated').rejectBy('visibilityIsPrivate');
1818
}
1919

2020
get sortedCourses(): CourseModel[] {

‎app/controllers/vote/course-extension-ideas.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export default class CourseExtensionIdeasController extends Controller {
3131
.uniq()
3232
.rejectBy('releaseStatusIsDeprecated')
3333
.rejectBy('releaseStatusIsAlpha')
34+
.rejectBy('visibilityIsPrivate')
3435
.sortBy('sortPositionForTrack');
3536
}
3637

‎app/models/course.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export default class CourseModel extends Model {
4444
@attr('string') declare shortName: string;
4545
@attr('string') declare slug: string;
4646
@attr('string') declare testerRepositoryFullName: string;
47+
@attr('string') declare visibility: 'public' | 'private';
4748

4849
@attr() declare testimonials: {
4950
author_name: string;
@@ -80,6 +81,9 @@ export default class CourseModel extends Model {
8081
@equal('releaseStatus', 'live') declare releaseStatusIsLive: boolean;
8182
@equal('releaseStatus', 'deprecated') declare releaseStatusIsDeprecated: boolean;
8283

84+
@equal('visibility', 'public') declare visibilityIsPublic: boolean;
85+
@equal('visibility', 'private') declare visibilityIsPrivate: boolean;
86+
8387
@service declare date: DateService;
8488

8589
get baseStages() {

‎package-lock.json‎

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎tests/acceptance/track-page/view-track-test.js‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,34 @@ module('Acceptance | track-page | view-track', function (hooks) {
140140
assert.notOk(trackPage.cards.mapBy('title').includes('Build your own Docker'));
141141
});
142142

143+
test('it does not show a challenge if it is private', async function (assert) {
144+
signIn(this.owner, this.server);
145+
146+
let redis = this.server.schema.courses.findBy({ slug: 'redis' });
147+
redis.update({ visibility: 'private' });
148+
149+
await visit('/tracks/python');
150+
assert.notOk(trackPage.cards.mapBy('title').includes('Build your own Redis'), 'private course should not be included');
151+
});
152+
153+
test('it does not show a challenge if it is private and user has repository', async function (assert) {
154+
signIn(this.owner, this.server);
155+
156+
let currentUser = this.server.schema.users.first();
157+
let python = this.server.schema.languages.findBy({ name: 'Python' });
158+
let redis = this.server.schema.courses.findBy({ slug: 'redis' });
159+
redis.update({ visibility: 'private' });
160+
161+
this.server.create('repository', {
162+
course: redis,
163+
language: python,
164+
user: currentUser,
165+
});
166+
167+
await visit('/tracks/python');
168+
assert.notOk(trackPage.cards.mapBy('title').includes('Build your own Redis'), 'private course should not be included');
169+
});
170+
143171
test('visiting from catalog page has no loading page', async function (assert) {
144172
let loadingIndicatorWasRendered = false;
145173

‎tests/acceptance/view-courses-test.js‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,4 +338,37 @@ module('Acceptance | view-courses', function (hooks) {
338338
assert.strictEqual(catalogPage.courseCards.length, 4, 'expected 4 course cards to be present');
339339
assert.notOk(catalogPage.courseCards.mapBy('name').includes('Build your own Docker'), 'docker should not be included');
340340
});
341+
342+
test('it should not show private courses in catalog', async function (assert) {
343+
testScenario(this.server);
344+
signIn(this.owner, this.server);
345+
346+
let redis = this.server.schema.courses.findBy({ slug: 'redis' });
347+
redis.update({ visibility: 'private' });
348+
349+
await catalogPage.visit();
350+
351+
assert.strictEqual(catalogPage.courseCards.length, 4, 'expected 4 course cards to be present');
352+
assert.notOk(catalogPage.courseCards.mapBy('name').includes('Build your own Redis'), 'redis should not be included');
353+
});
354+
355+
test('it should show private courses in catalog if user has repository', async function (assert) {
356+
testScenario(this.server);
357+
signIn(this.owner, this.server);
358+
359+
let currentUser = this.server.schema.users.first();
360+
let python = this.server.schema.languages.findBy({ name: 'Python' });
361+
let redis = this.server.schema.courses.findBy({ slug: 'redis' });
362+
redis.update({ visibility: 'private' });
363+
364+
this.server.create('repository', {
365+
course: redis,
366+
language: python,
367+
user: currentUser,
368+
});
369+
370+
await catalogPage.visit();
371+
372+
assert.ok(catalogPage.courseCards.mapBy('name').includes('Build your own Redis'), 'redis should be included');
373+
});
341374
});

‎tests/acceptance/view-user-profile-test.js‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,4 +250,35 @@ module('Acceptance | view-user-profile', function (hooks) {
250250
assert.strictEqual(userPage.courseProgressListItems.length, 1, 'only one course progress list item should be shown');
251251
assert.strictEqual(userPage.courseProgressListItems[0].name, 'Build your own grep', 'the course progress list item should be for grep');
252252
});
253+
254+
test('it does not show private courses in user profile', async function (assert) {
255+
testScenario(this.server);
256+
257+
let currentUser = this.server.schema.users.first();
258+
let go = this.server.schema.languages.findBy({ slug: 'go' });
259+
let redis = this.server.schema.courses.findBy({ slug: 'redis' });
260+
let grep = this.server.schema.courses.findBy({ slug: 'grep' });
261+
redis.update({ visibility: 'private' });
262+
263+
this.server.create('course-participation', {
264+
course: grep,
265+
language: go,
266+
user: currentUser,
267+
completedStageSlugs: grep.stages.models.sortBy('position').slice(0, 5).mapBy('slug'),
268+
lastSubmissionAt: new Date('2020-10-10'),
269+
});
270+
271+
this.server.create('course-participation', {
272+
course: redis,
273+
language: go,
274+
user: currentUser,
275+
completedAt: new Date('2020-01-01'),
276+
});
277+
278+
await userPage.visit({ username: 'rohitpaulk' });
279+
280+
assert.strictEqual(userPage.courseProgressListItems.length, 1, 'only one course progress list item should be shown');
281+
assert.strictEqual(userPage.courseProgressListItems[0].name, 'Build your own grep', 'the course progress list item should be for grep');
282+
assert.notOk(userPage.courseProgressListItems.mapBy('name').includes('Build your own Redis'), 'private course should not be included');
283+
});
253284
});

0 commit comments

Comments
 (0)