Skip to content

Commit 51b87d5

Browse files
committed
[us40] Support for rule modification
1 parent 1de7ba5 commit 51b87d5

8 files changed

+154
-38
lines changed

src/app/rules/rule.repository.spec.ts

+29
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,35 @@ describe('RuleRepository', () => {
120120
expect(rules).toEqual([]);
121121
});
122122
});
123+
124+
describe('update', () => {
125+
it('should update an existing rule', async () => {
126+
// Arrange
127+
const ruleRepository = MockRender(RuleRepository).point.componentInstance;
128+
// 2 calls to 'backup' expected, from create, and then from update
129+
mockBackupCall().times(2);
130+
let rule: Rule = {
131+
name: 'TestRule',
132+
category: ['Test1', 'ChildTest1'],
133+
script: 'return true'
134+
};
135+
await ruleRepository.create(rule)
136+
rule.name = 'TestRule edited';
137+
138+
// Act
139+
await ruleRepository.update(rule)
140+
141+
// Assert
142+
let rules = await db.rules.toArray();
143+
expect(rules)
144+
.toEqual([{
145+
id: 1,
146+
name: 'TestRule edited',
147+
category: ['Test1', 'ChildTest1'],
148+
script: 'return true'
149+
}]);
150+
})
151+
})
123152
});
124153

125154
export function mockRuleRepository() {

src/app/rules/rule.repository.ts

+7
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,11 @@ export class RuleRepository {
3030
this.databaseBackupAndRestoreService.backup().subscribe();
3131
}
3232
}
33+
34+
async update(rule: Rule) {
35+
if (rule.id) {
36+
await db.rules.update(rule.id, rule);
37+
this.databaseBackupAndRestoreService.backup().subscribe();
38+
}
39+
}
3340
}

src/app/rules/rule.service.spec.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,8 @@ export function mockRuleService() {
307307
runAll: ruleServiceMock.runAll,
308308
create: ruleServiceMock.create,
309309
findAll: ruleServiceMock.findAll,
310-
delete: ruleServiceMock.delete
310+
delete: ruleServiceMock.delete,
311+
update: ruleServiceMock.update
311312
}
312313
});
313314
return ruleServiceMock;

src/app/rules/rule.service.ts

+5
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ export class RuleService {
6868
return this.ruleRepository.delete(rule);
6969
}
7070

71+
update(rule: Rule): Promise<void> {
72+
return this.ruleRepository.update(rule);
73+
}
74+
7175
/**
7276
* Run the given rules on the given files and return the associated category for each file that got a matching rule
7377
*/
@@ -167,6 +171,7 @@ export class RuleService {
167171
}
168172

169173
// TODO: move and refactor duplicate to FileService
174+
170175
private findOrCreateCategories(categories: string[], categoryId: string): Observable<string> {
171176
let categoryName = categories.shift();
172177
if (categoryName !== undefined) {

src/app/rules/rules.component.html

+18-12
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,22 @@
55
<div class="smd-main-content">
66
<h1>Setup rules</h1>
77
<div class="actionButtons">
8-
<button (click)="showCreate = true" [disabled]="showCreate" mat-raised-button>Create new rule</button>
8+
<button (click)="showCreate()" [disabled]="ruleToCreateOrUpdate" mat-raised-button>Create new rule</button>
99
&nbsp;
1010
<button (click)="runAll()" color="primary" mat-raised-button>Run all</button>
1111
</div>
12-
<form (ngSubmit)="createNewRule()" *ngIf="showCreate">
12+
<form (ngSubmit)="createOrUpdateRule()" *ngIf="ruleToCreateOrUpdate">
1313
<div class="nameAndCategoryFields">
1414
<mat-form-field>
1515
<mat-label>Name</mat-label>
16-
<input [(ngModel)]="ruleToCreate.name" matInput name="name" required>
16+
<input [(ngModel)]="ruleToCreateOrUpdate.name" matInput name="name" required>
1717
</mat-form-field>
1818
&nbsp;
1919
<mat-form-field>
2020
<mat-label>Category</mat-label>
2121
<mat-chip-grid #chipGrid aria-label="Enter category">
22-
<mat-chip-row *ngFor="let cat of ruleToCreate.category">
23-
{{cat}}
22+
<mat-chip-row *ngFor="let cat of ruleToCreateOrUpdate.category">
23+
{{ cat }}
2424
<button matChipRemove>
2525
<mat-icon>cancel</mat-icon>
2626
</button>
@@ -34,28 +34,34 @@ <h1>Setup rules</h1>
3434
</div>
3535
<mat-form-field class="fullWidth">
3636
<mat-label>Script</mat-label>
37-
<textarea [(ngModel)]="ruleToCreate.script" matInput name="script" required></textarea>
37+
<textarea [(ngModel)]="ruleToCreateOrUpdate.script" matInput name="script" required></textarea>
3838
</mat-form-field>
3939
<div class="actionButtons">
40-
<button (click)="showCreate = false" mat-raised-button>Cancel</button>
40+
<button (click)="cancelCreateOrUpdate()" mat-raised-button>Cancel</button>
4141
&nbsp;
42-
<button color="primary" mat-raised-button type="submit">Create</button>
42+
<button *ngIf="ruleToCreateOrUpdate.id" color="primary" mat-raised-button type="submit">Update</button>
43+
<button *ngIf="!ruleToCreateOrUpdate.id" color="primary" mat-raised-button type="submit">Create</button>
4344
</div>
4445
</form>
4546
<mat-accordion [multi]="true">
4647
<mat-expansion-panel *ngFor="let rule of rules">
4748
<mat-expansion-panel-header>
4849
<mat-panel-title>
49-
{{rule.name}}
50+
{{ rule.name }}
5051
</mat-panel-title>
5152
<mat-panel-description>
52-
<span *ngFor="let cat of rule.category; first as first"><span *ngIf="!first"> &nbsp;&gt; </span>{{cat}}</span>
53+
<span *ngFor="let cat of rule.category; first as first"><span
54+
*ngIf="!first"> &nbsp;&gt; </span>{{ cat }}</span>
5355
</mat-panel-description>
5456
</mat-expansion-panel-header>
5557
<div class="ruleScript">
56-
{{rule.script}}
58+
{{ rule.script }}
59+
</div>
60+
<div class="ruleButtons">
61+
<button (click)="update(rule)" mat-raised-button>Edit</button>
62+
&nbsp;
63+
<button (click)="delete(rule)" color="warn" mat-raised-button>Delete</button>
5764
</div>
58-
<button (click)="delete(rule)" class="ruleDeleteButton" color="warn" mat-raised-button>Delete</button>
5965
</mat-expansion-panel>
6066
</mat-accordion>
6167
</div>

src/app/rules/rules.component.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ mat-form-field {
3232
width: 100%;
3333
}
3434

35-
.ruleDeleteButton {
35+
.ruleButtons {
3636
margin-top: 20px;
3737
}

src/app/rules/rules.component.spec.ts

+58-6
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ describe('RulesComponent', () => {
9393
// Act
9494
await page.clickOnCreateNewRule();
9595
fixture.detectChanges();
96-
await page.setCreateRuleName('New rule');
97-
await page.setCreateRuleCategory(['Cat1', 'ChildCat1']);
98-
await page.setCreateRuleScript('return fileName === "child_cat_1.txt"');
96+
await page.setRuleName('New rule');
97+
await page.setRuleCategory(['Cat1', 'ChildCat1']);
98+
await page.setRuleScript('return fileName === "child_cat_1.txt"');
9999
await page.clickOnCreate();
100100

101101
// Assert
@@ -137,6 +137,48 @@ describe('RulesComponent', () => {
137137
expect(Page.getRuleNames())
138138
.toEqual([]);
139139
}))
140+
141+
it('should update an existing rule', fakeAsync(async () => {
142+
// Arrange
143+
let ruleService = mockRuleService();
144+
145+
let rule: Rule = {
146+
id: 1,
147+
name: 'Rule1',
148+
category: ['Cat1', 'ChildCat1'],
149+
script: 'return fileName === "child_cat_1.txt"'
150+
};
151+
when(() => ruleService.findAll()).thenResolve([rule]);
152+
153+
// A refresh is expected after update
154+
let editedRule: Rule = {
155+
id: 1,
156+
name: 'Rule1 edited',
157+
category: ['Cat1', 'ChildCat1'],
158+
script: 'return fileName === "child_cat_1.txt"'
159+
};
160+
when(() => ruleService.findAll()).thenResolve([editedRule]);
161+
162+
when(() => ruleService.update(editedRule)).thenResolve(undefined);
163+
164+
let fixture = MockRender(RulesComponent);
165+
tick();
166+
167+
let page = new Page(fixture);
168+
169+
// Act
170+
await page.clickOnEditFirstRule();
171+
fixture.detectChanges();
172+
await page.setRuleName('Rule1 edited');
173+
await page.clickOnUpdate();
174+
175+
// Assert
176+
// No failure in mock setup
177+
tick();
178+
fixture.detectChanges();
179+
expect(Page.getRuleNames())
180+
.toEqual(['Rule1 edited']);
181+
}))
140182
});
141183

142184

@@ -195,12 +237,17 @@ class Page {
195237
await button.click();
196238
}
197239

198-
async setCreateRuleName(name: string) {
240+
async clickOnEditFirstRule() {
241+
let button = await this.loader.getHarness(MatButtonHarness.with({text: 'Edit'}));
242+
await button.click();
243+
}
244+
245+
async setRuleName(name: string) {
199246
let input = await this.getInputByFloatingLabel('Name');
200247
await input.setValue(name);
201248
}
202249

203-
async setCreateRuleCategory(category: string[]) {
250+
async setRuleCategory(category: string[]) {
204251
// let inputHarness = await this.loader.getHarness(MatInputHarness.with({placeholder: 'Select category...'}));
205252
let chipGridHarness = await this.loader.getHarness(MatChipGridHarness);
206253
let inputHarness = await chipGridHarness.getInput()
@@ -213,7 +260,7 @@ class Page {
213260
}
214261
}
215262

216-
async setCreateRuleScript(script: string) {
263+
async setRuleScript(script: string) {
217264
let inputHarness = await this.getInputByFloatingLabel('Script');
218265
await inputHarness.setValue(script);
219266
}
@@ -223,6 +270,11 @@ class Page {
223270
await button.click();
224271
}
225272

273+
async clickOnUpdate() {
274+
let button = await this.loader.getHarness(MatButtonHarness.with({text: 'Update'}));
275+
await button.click();
276+
}
277+
226278
private async getInputByFloatingLabel(floatingLabelText: string | RegExp) {
227279
let formFieldHarness = await this.loader.getHarness(MatFormFieldHarness.with({floatingLabelText: floatingLabelText}));
228280
let control = await formFieldHarness.getControl();

src/app/rules/rules.component.ts

+34-18
Original file line numberDiff line numberDiff line change
@@ -15,36 +15,36 @@ export class RulesComponent {
1515
readonly separatorKeysCodes = [ENTER] as const;
1616

1717
rules: Rule[] = [];
18-
showCreate: boolean = false;
19-
ruleToCreate: Rule = {
20-
name: '',
21-
category: [],
22-
script: ''
23-
};
18+
ruleToCreateOrUpdate?: Rule = undefined;
2419

2520
constructor(private ruleService: RuleService) {
2621
this.refresh();
2722
}
2823

29-
createNewRule() {
30-
this.ruleService.create(this.ruleToCreate)
31-
.then(() => {
32-
this.showCreate = false;
33-
this.ruleToCreate = {
34-
name: '',
35-
category: [],
36-
script: ''
37-
};
38-
this.refresh();
39-
})
24+
createOrUpdateRule() {
25+
if (this.ruleToCreateOrUpdate) {
26+
if (!this.ruleToCreateOrUpdate.id) {
27+
this.ruleService.create(this.ruleToCreateOrUpdate)
28+
.then(() => {
29+
this.cancelCreateOrUpdate();
30+
this.refresh();
31+
})
32+
} else {
33+
this.ruleService.update(this.ruleToCreateOrUpdate)
34+
.then(() => {
35+
this.cancelCreateOrUpdate();
36+
this.refresh();
37+
})
38+
}
39+
}
4040
}
4141

4242
runAll() {
4343
this.ruleService.runAll().subscribe();
4444
}
4545

4646
add(event: MatChipInputEvent) {
47-
this.ruleToCreate.category.push(event.value);
47+
this.ruleToCreateOrUpdate?.category.push(event.value);
4848
event.chipInput.clear();
4949
}
5050

@@ -55,6 +55,22 @@ export class RulesComponent {
5555
})
5656
}
5757

58+
update(rule: Rule) {
59+
this.ruleToCreateOrUpdate = rule;
60+
}
61+
62+
cancelCreateOrUpdate() {
63+
this.ruleToCreateOrUpdate = undefined;
64+
}
65+
66+
showCreate() {
67+
this.ruleToCreateOrUpdate = {
68+
name: '',
69+
category: [],
70+
script: ''
71+
};
72+
}
73+
5874
private refresh() {
5975
this.ruleService.findAll()
6076
.then(rules => {

0 commit comments

Comments
 (0)