Skip to content

Commit e5a1b35

Browse files
Merge branch 'develop' into migrate/profile_view_PR-3
2 parents 8bb1a75 + b28ea50 commit e5a1b35

File tree

10 files changed

+261
-8
lines changed

10 files changed

+261
-8
lines changed

app/components/identity/reload.hbs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,10 @@
22
<div class='identity-box-desc' data-test-reload-desc>Reload to complete and
33
verify the link between Profile Service and RealDevSquad Service.</div>
44
<button
5-
class='identity-box-button' type="button" {{on 'click' this.handleReload}}
6-
>Reload</button>
5+
class='identity-box-button'
6+
data-test-blocked-button
7+
type="button"
8+
{{on "click" (fn this.setState "step1")}}
9+
>
10+
Retry
11+
</button>

app/components/spinner.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<i class="fa fa-spinner fa-spin"></i>
1+
<i class="fa fa-spinner fa-spin spinner" ...attributes></i>

app/constants/error-messages.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export const ERROR_MESSAGES = {
22
somethingWentWrong: 'Something went wrong!',
33
usernameGeneration: 'Username cannot be generated',
4+
notLoggedIn: 'You are not logged in. Please login to continue.',
45
};

app/routes/identity.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import Route from '@ember/routing/route';
22
import { inject as service } from '@ember/service';
33
import { APPS } from '../constants/urls';
4+
import redirectAuth from '../utils/redirect-auth';
5+
import { TOAST_OPTIONS } from '../constants/toast-options';
6+
import { ERROR_MESSAGES } from '../constants/error-messages';
47
export default class IdentityRoute extends Route {
58
@service router;
69
@service login;
710
@service fastboot;
811

912
beforeModel(transition) {
1013
if (transition?.to?.queryParams?.dev !== 'true') {
11-
this.router.transitionTo('page-not-found');
12-
return;
14+
this.router.transitionTo('/page-not-found');
1315
}
1416
}
1517

@@ -29,7 +31,8 @@ export default class IdentityRoute extends Route {
2931

3032
if (!response.ok) {
3133
if (response.status === 401) {
32-
this.router.transitionTo('index');
34+
this.toast.error(ERROR_MESSAGES.notLoggedIn, '', TOAST_OPTIONS);
35+
setTimeout(redirectAuth, 2000);
3336
return null;
3437
}
3538
throw new Error(`HTTP error! status: ${response.status}`);

app/styles/app.css

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,33 @@ button {
9393
#toast-container > div {
9494
opacity: 1;
9595
}
96+
97+
.loading {
98+
display: flex;
99+
flex-direction: column;
100+
justify-content: center;
101+
align-items: center;
102+
height: 90vh;
103+
width: 100%;
104+
text-align: center;
105+
}
106+
107+
.spinner {
108+
display: inline-block;
109+
width: 1.875 rem;
110+
height: 1.875 rem;
111+
border: 4px solid rgb(0 0 0 / 30%);
112+
border-radius: 50%;
113+
border-top: 4px solid var(--color-black);
114+
animation: spin 1s linear infinite;
115+
}
116+
117+
@keyframes spin {
118+
0% {
119+
transform: rotate(0deg);
120+
}
121+
122+
100% {
123+
transform: rotate(360deg);
124+
}
125+
}

app/styles/identity.module.css

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@
231231
}
232232

233233
.identity-box-input {
234-
border: 1px solid var(--color-black-50);
234+
border: 1px solid var(--color-black-25);
235235
background: var(--color-white);
236236
width: 50%;
237237
margin-top: 16px;
@@ -261,6 +261,31 @@
261261
width: 100%;
262262
}
263263

264+
.identity-chaincode-copy-box {
265+
display: flex;
266+
width: 60%;
267+
}
268+
269+
.identity-chaincode-box {
270+
display: flex;
271+
position: relative;
272+
justify-content: space-between;
273+
align-items: center;
274+
padding-left: 0.5rem;
275+
padding-right: 0.5rem;
276+
margin-top: 16px;
277+
border: 1px solid var(--color-black-25);
278+
background: var(--color-white);
279+
width: 90%;
280+
height: 2rem;
281+
color: var(--color-black);
282+
font-family: Inter, sans-serif;
283+
font-size: 1rem;
284+
font-style: normal;
285+
font-weight: 500;
286+
line-height: normal;
287+
}
288+
264289
@media (width <= 460px) {
265290
.identity-box-input {
266291
width: 90%;

app/templates/identity.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
{{else if (eq this.initialState 'verified')}}
2929
<Identity::Verified />
3030
{{else if (eq this.initialState 'blocked')}}
31-
<Identity::Blocked />
31+
<Identity::Blocked @setState={{this.setState}} />
3232
{{/if}}
3333
</div>
3434

app/templates/loading.hbs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<div class="loading">
2+
<Spinner />
3+
</div>

app/utils/redirect-auth.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import { AUTH } from '../constants/urls';
22

3+
/**
4+
* Redirects to the GitHub authorization URL with the current window's location
5+
* as the redirect URL.
6+
* @function redirectAuth
7+
* @memberof utils
8+
*/
9+
310
export default function redirectAuth() {
411
let authUrl = AUTH.GITHUB_SIGN_IN;
512
if (typeof window !== 'undefined' && window.location) {

tests/unit/routes/identity-test.js

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import { module, test } from 'qunit';
2+
import { setupTest } from 'website-www/tests/helpers';
3+
import { TOAST_OPTIONS } from 'website-www/constants/toast-options';
4+
import { APPS } from 'website-www/constants/urls';
5+
import sinon from 'sinon';
6+
7+
module('Unit | Route | identity', function (hooks) {
8+
setupTest(hooks);
9+
10+
hooks.beforeEach(function () {
11+
this.route = this.owner.lookup('route:identity');
12+
13+
this.route.router = {
14+
transitionTo: sinon.stub(),
15+
};
16+
this.route.toast = {
17+
error: sinon.stub(),
18+
};
19+
this.route.fastboot = {
20+
isFastBoot: false,
21+
};
22+
});
23+
24+
test('it exists', function (assert) {
25+
assert.ok(this.route);
26+
});
27+
28+
test('beforeModel redirects to page-not-found when dev param is not true', function (assert) {
29+
const transition = {
30+
to: {
31+
queryParams: {
32+
dev: 'false',
33+
},
34+
},
35+
};
36+
37+
this.route.beforeModel(transition);
38+
assert.ok(
39+
this.route.router.transitionTo.calledWith('/page-not-found'),
40+
'should redirect to page-not-found',
41+
);
42+
});
43+
44+
test('beforeModel allows navigation when dev param is true', function (assert) {
45+
const transition = {
46+
to: {
47+
queryParams: {
48+
dev: 'true',
49+
},
50+
},
51+
};
52+
53+
this.route.beforeModel(transition);
54+
assert.notOk(this.route.router.transitionTo.called, 'should not redirect');
55+
});
56+
57+
test('model returns null in FastBoot', async function (assert) {
58+
this.route.fastboot.isFastBoot = true;
59+
const result = await this.route.model();
60+
assert.strictEqual(result, null, 'should return null in FastBoot');
61+
});
62+
63+
test('model handles 401 unauthorized response', async function (assert) {
64+
const fetchStub = sinon.stub(window, 'fetch').resolves({
65+
ok: false,
66+
status: 401,
67+
});
68+
69+
const result = await this.route.model();
70+
71+
assert.strictEqual(result, null, 'should return null');
72+
assert.ok(
73+
this.route.toast.error.calledWith(
74+
'You are not logged in. Please login to continue.',
75+
'',
76+
TOAST_OPTIONS,
77+
),
78+
'should show error toast',
79+
);
80+
81+
fetchStub.restore();
82+
});
83+
84+
test('model handles successful response with invalid discord role', async function (assert) {
85+
const fetchStub = sinon.stub(window, 'fetch').resolves({
86+
ok: true,
87+
json: () =>
88+
Promise.resolve({
89+
roles: {
90+
in_discord: false,
91+
},
92+
}),
93+
});
94+
95+
const result = await this.route.model();
96+
97+
assert.strictEqual(result, null, 'should return null');
98+
assert.ok(
99+
this.route.router.transitionTo.calledWith('index'),
100+
'should redirect to index',
101+
);
102+
103+
fetchStub.restore();
104+
});
105+
106+
test('model handles network error', async function (assert) {
107+
const fetchStub = sinon
108+
.stub(window, 'fetch')
109+
.rejects(new Error('Network error'));
110+
111+
const result = await this.route.model();
112+
113+
assert.deepEqual(result, null, 'should return null');
114+
assert.ok(
115+
this.route.router.transitionTo.calledWith('index'),
116+
'should redirect to index',
117+
);
118+
119+
fetchStub.restore();
120+
});
121+
122+
test('model handles non-401 error response', async function (assert) {
123+
const fetchStub = sinon.stub(window, 'fetch').resolves({
124+
ok: false,
125+
status: 500,
126+
});
127+
128+
const result = await this.route.model();
129+
130+
assert.strictEqual(result, null, 'should return null');
131+
assert.ok(
132+
this.route.router.transitionTo.calledWith('index'),
133+
'should redirect to index',
134+
);
135+
136+
fetchStub.restore();
137+
});
138+
139+
test('model handles successful response with valid discord role', async function (assert) {
140+
const mockData = {
141+
roles: {
142+
in_discord: true,
143+
},
144+
someOtherData: 'test',
145+
};
146+
147+
const fetchStub = sinon.stub(window, 'fetch').resolves({
148+
ok: true,
149+
json: () => Promise.resolve(mockData),
150+
});
151+
152+
const result = await this.route.model();
153+
154+
assert.deepEqual(result, mockData, 'should return the API response data');
155+
assert.ok(fetchStub.called, 'fetch should be called');
156+
157+
const [actualUrl, actualOptions] = fetchStub.firstCall.args;
158+
159+
assert.strictEqual(
160+
actualUrl,
161+
`${APPS.API_BACKEND}/users?profile=true`,
162+
'should call correct URL',
163+
);
164+
165+
assert.deepEqual(
166+
actualOptions,
167+
{
168+
credentials: 'include',
169+
headers: {
170+
Accept: 'application/json',
171+
'Content-Type': 'application/json',
172+
},
173+
},
174+
'should pass correct options',
175+
);
176+
177+
fetchStub.restore();
178+
});
179+
});

0 commit comments

Comments
 (0)