Skip to content

Commit 2b940df

Browse files
authored
Merge pull request #118 from ioet/29/get-technologies
closes #29
2 parents b168b82 + afc20cc commit 2b940df

28 files changed

+483
-58
lines changed

.github/workflows/CD-time-tracker-ui.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ jobs:
3232
SCOPES: ${{ secrets.scopes }}
3333
CLIENT_ID: ${{ secrets.client_id }}
3434
AUTHORITY: ${{ secrets.authority }}
35+
STACK_EXCHANGE_ID: ${{ secrets.stack_exchange_id }}
36+
STACK_EXCHANGE_ACCESS_TOKEN: ${{ secrets.stack_exchange_access_token }}
3537
run: |
3638
chmod +x ./scripts/populate-keys.sh
3739
sh ./scripts/populate-keys.sh

.github/workflows/CI-mutation-tests.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ jobs:
2626
SCOPES: ${{ secrets.scopes }}
2727
CLIENT_ID: ${{ secrets.client_id }}
2828
AUTHORITY: ${{ secrets.authority }}
29+
STACK_EXCHANGE_ID: ${{ secrets.stack_exchange_id }}
30+
STACK_EXCHANGE_ACCESS_TOKEN: ${{ secrets.stack_exchange_access_token }}
2931
run: sudo sh scripts/populate-keys.sh
3032

3133
- name: Run the test

.github/workflows/CI-time-tracker-ui.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,14 @@ jobs:
4444
SCOPES: ${{ secrets.scopes }}
4545
CLIENT_ID: ${{ secrets.client_id }}
4646
AUTHORITY: ${{ secrets.authority }}
47+
STACK_EXCHANGE_ID: ${{ secrets.stack_exchange_id }}
48+
STACK_EXCHANGE_ACCESS_TOKEN: ${{ secrets.stack_exchange_access_token }}
4749
run: sudo sh scripts/populate-keys.sh
4850

4951
- name: Run the test
5052
run: npm run ci-test --if-present
5153

5254
- name: Generate coverage report
53-
env:
55+
env:
5456
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
5557
run: bash <(curl -s https://codecov.io/bash)

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ Create a file keys.ts with the content pinned in our slack channel:
4040
export const AUTHORITY = 'XXX';
4141
export const CLIENT_ID = 'XXX';
4242
export const SCOPES = ['XXX'];
43+
export const STACK_EXCHANGE_ID = 'XXX';
44+
export const STACK_EXCHANGE_ACCESS_TOKEN = 'XXX';
4345
```
4446

4547
### Prepare your environment for vscode

scripts/populate-keys.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@
44
echo 'export const AUTHORITY = "'$AUTHORITY'";' >> src/environments/keys.ts
55
echo 'export const CLIENT_ID = "'$CLIENT_ID'";' >> src/environments/keys.ts
66
echo 'export const SCOPES = ["'$SCOPES'"];' >> src/environments/keys.ts
7+
echo 'export const STACK_EXCHANGE_ID = "'$STACK_EXCHANGE_ID'";' >> src/environments/keys.ts
8+
echo 'export const STACK_EXCHANGE_ACCESS_TOKEN = "'$STACK_EXCHANGE_ACCESS_TOKEN'";' >> src/environments/keys.ts
79
cat src/environments/keys.ts

src/app/app.module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import { HomeComponent } from './modules/home/home.component';
3232
import { LoginComponent } from './modules/login/login.component';
3333
import { ActivityEffects } from './modules/activities-management/store/activity-management.effects';
3434
import { ProjectEffects } from './modules/project-management/store/project.effects';
35+
import { TechnologyEffects } from './modules/shared/store/technology.effects';
3536
import { reducers, metaReducers } from './reducers';
3637
import { environment } from '../environments/environment';
3738
import { CustomerComponent } from './modules/customer-managment/pages/customer.component';
@@ -99,7 +100,7 @@ import { CreateProjectTypeComponent } from './modules/customer-managment/compone
99100
maxAge: 15, // Retains last 15 states
100101
})
101102
: [],
102-
EffectsModule.forRoot([ProjectEffects, ActivityEffects]),
103+
EffectsModule.forRoot([ProjectEffects, ActivityEffects, TechnologyEffects]),
103104
],
104105
providers: [],
105106
bootstrap: [AppComponent],

src/app/modules/shared/components/details-fields/details-fields.component.html

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,38 @@
3636
aria-describedby="inputGroup-sizing-sm"
3737
/>
3838
</div>
39-
<div class="input-group input-group-sm mb-3">
39+
40+
<div class="input-group input-group-sm">
4041
<div class="input-group-prepend">
4142
<span class="input-group-text span-width" id="inputGroup-sizing-sm">Technology</span>
4243
</div>
4344
<input
44-
formControlName="technology"
45+
(keypress)="getTechnologies($event.target.value)"
4546
type="text"
4647
class="form-control"
4748
aria-label="Small"
4849
aria-describedby="inputGroup-sizing-sm"
4950
/>
5051
</div>
52+
53+
<div *ngIf="isLoading">LOADING...</div>
54+
<div *ngIf="technology" class="d-flex flex-column technology-content">
55+
<div
56+
*ngFor="let item of technology.items"
57+
(click)="setTechnology(item.name)"
58+
class="technology-list"
59+
[ngClass]="{ active: selectedTechnology.includes(item.name) }"
60+
>
61+
{{ item.name }}
62+
</div>
63+
</div>
64+
<div class="tags-content d-flex flex-wrap">
65+
<div *ngFor="let technology of selectedTechnology; let tagIndex = index" class="tag">
66+
<span class="mr-3">{{ technology }}</span>
67+
<i class="fas fa-times text-white" (click)="removeTag(tagIndex)"></i>
68+
</div>
69+
</div>
70+
5171
<div class="form-group">
5272
<label for="NotesTextarea">Notes</label>
5373
<textarea formControlName="comments" class="form-control" id="NotesTextarea" rows="3"></textarea>

src/app/modules/shared/components/details-fields/details-fields.component.scss

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
11
@import '../../../../../styles/colors.scss';
2+
3+
@mixin tagTechnology() {
4+
background-color: $modal-button-secondary;
5+
color: #ffffff;
6+
7+
&:hover {
8+
opacity: 0.8;
9+
}
10+
}
11+
212
.span-width {
313
width: 6rem;
414
background-image: $background-pantone;
@@ -18,3 +28,44 @@
1828
background-color: $modal-button-secondary;
1929
color: white;
2030
}
31+
32+
.technology-content {
33+
box-shadow: 0 3px 8px 0 rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.08);
34+
margin: 0 0 2rem 6rem;
35+
max-height: 7.5rem;
36+
overflow-y: auto;
37+
38+
.technology-list {
39+
cursor: pointer;
40+
font-size: 0.8rem;
41+
margin-bottom: 0.1rem;
42+
padding: 0.2rem 0.5rem;
43+
44+
&:hover {
45+
opacity: 0.7;
46+
}
47+
}
48+
49+
.active {
50+
background-color: #efefef;
51+
}
52+
}
53+
54+
.tags-content {
55+
margin: 2rem 0;
56+
57+
div {
58+
@include tagTechnology();
59+
60+
border-radius: 0.2rem;
61+
box-shadow: 2px 2px 5px 0px rgba(0, 0, 0, 0.75);
62+
font-size: 0.8rem;
63+
padding: 0.1rem 1rem 0.2rem 1.5rem;
64+
position: relative;
65+
margin: 0 0.5rem 0.5rem 0;
66+
67+
i {
68+
cursor: pointer;
69+
}
70+
}
71+
}
Lines changed: 90 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,47 @@
11
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
import { provideMockStore, MockStore } from '@ngrx/store/testing';
23
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
34

5+
import { TechnologyState } from '../../store/technology.reducers';
6+
import { allTechnologies } from '../../store/technology.selectors';
47
import { DetailsFieldsComponent } from './details-fields.component';
8+
import { FilterProjectPipe } from '../../../shared/pipes';
9+
import * as actions from '../../store/technology.actions';
510

611
describe('DetailsFieldsComponent', () => {
712
let component: DetailsFieldsComponent;
813
let fixture: ComponentFixture<DetailsFieldsComponent>;
14+
let store: MockStore<TechnologyState>;
15+
let mockTechnologySelector;
16+
let length;
17+
18+
const state = {
19+
technologyList: { items: [{ name: 'java' }] },
20+
isLoading: false,
21+
};
22+
923
const initialData = {
1024
project: '',
1125
activity: '',
1226
ticket: '',
13-
technology: '',
14-
comments: ''
27+
comments: '',
1528
};
1629

1730
const newData = {
1831
project: 'Ernst&Young',
1932
activity: 'development',
2033
ticket: 'WA-15',
21-
technology: 'Angular',
22-
comments: 'No notes'
34+
comments: 'No notes',
2335
};
2436

2537
beforeEach(async(() => {
2638
TestBed.configureTestingModule({
27-
declarations: [DetailsFieldsComponent],
28-
imports: [
29-
FormsModule,
30-
ReactiveFormsModule
31-
],
39+
declarations: [DetailsFieldsComponent, FilterProjectPipe],
40+
providers: [provideMockStore({ initialState: state })],
41+
imports: [FormsModule, ReactiveFormsModule],
3242
}).compileComponents();
43+
store = TestBed.inject(MockStore);
44+
mockTechnologySelector = store.overrideSelector(allTechnologies, state);
3345
}));
3446

3547
beforeEach(() => {
@@ -42,12 +54,6 @@ describe('DetailsFieldsComponent', () => {
4254
expect(component).toBeTruthy();
4355
});
4456

45-
it('should emit saveEntry event', () => {
46-
spyOn(component.saveEntry, 'emit');
47-
component.onSubmit();
48-
expect(component.saveEntry.emit).toHaveBeenCalledWith(initialData);
49-
});
50-
5157
it('should emit ngOnChange without data', () => {
5258
component.entryToEdit = null;
5359
component.ngOnChanges();
@@ -59,4 +65,73 @@ describe('DetailsFieldsComponent', () => {
5965
component.ngOnChanges();
6066
expect(component.entryForm.value).toEqual(newData);
6167
});
68+
69+
it('should dispatch FindTechnology action #getTechnologies', () => {
70+
const value = 'java';
71+
spyOn(store, 'dispatch');
72+
length = value.length;
73+
component.getTechnologies(value);
74+
75+
expect(store.dispatch).toHaveBeenCalledWith(new actions.FindTechnology(value));
76+
});
77+
78+
it('should NOT dispatch FindTechnology action #getTechnologies', () => {
79+
const value = 'j';
80+
spyOn(store, 'dispatch');
81+
length = value.length;
82+
component.getTechnologies(value);
83+
84+
expect(store.dispatch).not.toHaveBeenCalledWith(new actions.FindTechnology(value));
85+
});
86+
87+
it('should add a new tag #setTechnology', () => {
88+
const name = 'ngrx';
89+
component.selectedTechnology = ['java', 'javascript'];
90+
component.selectedTechnology.indexOf(name);
91+
length = component.selectedTechnology.length;
92+
component.setTechnology(name);
93+
expect(component.selectedTechnology.length).toBe(3);
94+
});
95+
96+
it('should NOT add a new tag #setTechnology', () => {
97+
const name = 'ngrx';
98+
component.selectedTechnology = [
99+
'java',
100+
'javascript',
101+
'angular',
102+
'angular-ui',
103+
'typescript',
104+
'scss',
105+
'bootstrap',
106+
'jasmine',
107+
'karme',
108+
'github',
109+
];
110+
component.selectedTechnology.indexOf(name);
111+
length = component.selectedTechnology.length;
112+
component.setTechnology(name);
113+
expect(component.selectedTechnology.length).toBe(10);
114+
});
115+
116+
it('should call the removeTag function #setTechnology', () => {
117+
const name = 'java';
118+
component.selectedTechnology = ['java', 'javascript'];
119+
const index = component.selectedTechnology.indexOf(name);
120+
spyOn(component, 'removeTag');
121+
component.setTechnology(name);
122+
expect(component.removeTag).toHaveBeenCalledWith(index);
123+
});
124+
125+
it('should call the removeTag() function #removeTag', () => {
126+
const index = 1;
127+
component.selectedTechnology = ['java', 'angular'];
128+
component.removeTag(index);
129+
expect(component.selectedTechnology.length).toBe(1);
130+
});
131+
132+
it('should emit saveEntry event', () => {
133+
spyOn(component.saveEntry, 'emit');
134+
component.onSubmit();
135+
expect(component.saveEntry.emit).toHaveBeenCalledWith(initialData);
136+
});
62137
});

0 commit comments

Comments
 (0)