Skip to content

Commit

Permalink
NAS-125370 / 24.04 / Integration tests for autocomplete (#9354)
Browse files Browse the repository at this point in the history
  • Loading branch information
undsoft authored Dec 22, 2023
1 parent e9bd0ca commit 87a89f1
Show file tree
Hide file tree
Showing 15 changed files with 464 additions and 226 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<ix-icon *ngIf="queryInputValue && !hasQueryErrors" name="done"></ix-icon>
</span>

<div #inputArea class="input-area" (click)="startSuggestionsCompletion()">
<div #inputArea class="input-area" (focusin)="startSuggestionsCompletion()" (click)="startSuggestionsCompletion()">
<mat-card *ngIf="showDatePicker$ | async" class="calendar">
<mat-calendar (selectedChange)="dateSelected($event)"></mat-calendar>
</mat-card>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import {
Output,
ViewChild,
} from '@angular/core';
import { autocompletion, closeBrackets, startCompletion } from '@codemirror/autocomplete';
import {
autocompletion, closeBrackets, CompletionContext, startCompletion,
} from '@codemirror/autocomplete';
import { linter } from '@codemirror/lint';
import { EditorState, StateEffect, StateField } from '@codemirror/state';
import {
Expand Down Expand Up @@ -42,19 +44,19 @@ export class AdvancedSearchComponent<T> implements OnInit {

@ViewChild('inputArea', { static: true }) inputArea: ElementRef<HTMLElement>;

hasQueryErrors = false;
queryInputValue: string;
protected hasQueryErrors = false;
protected queryInputValue: string;
errorMessages: QueryParsingError[] | null = null;
editorView: EditorView;
protected editorView: EditorView;

showDatePicker$ = this.advancedSearchAutocomplete.showDatePicker$;
protected showDatePicker$ = this.advancedSearchAutocomplete.showDatePicker$;

get editorHasValue(): boolean {
return (this.editorView.state.doc as unknown as { text: string[] })?.text?.[0] !== '';
return this.editorView.state.doc?.length > 0;
}

constructor(
private queryParser: QueryParserService,
private queryParser: QueryParserService<T>,
private queryToApi: QueryToApiService<T>,
private advancedSearchAutocomplete: AdvancedSearchAutocompleteService<T>,
private cdr: ChangeDetectorRef,
Expand All @@ -67,11 +69,8 @@ export class AdvancedSearchComponent<T> implements OnInit {
this.advancedSearchAutocomplete.setEditorView(this.editorView);

if (this.query) {
this.setEditorContents(
this.queryParser.formatFiltersToQuery(
this.query as QueryFilters<never>,
this.properties as SearchProperty<never>[],
),
this.replaceEditorContents(
this.queryParser.formatFiltersToQuery(this.query, this.properties),
);
}
}
Expand Down Expand Up @@ -106,7 +105,7 @@ export class AdvancedSearchComponent<T> implements OnInit {
const advancedSearchLinter = linter((view) => view.state.field(diagnosticField));

const autocompleteExtension = autocompletion({
override: [this.advancedSearchAutocomplete.setCompletionSource.bind(this.advancedSearchAutocomplete)],
override: [(context: CompletionContext) => this.advancedSearchAutocomplete.getCompletions(context)],
icons: false,
});

Expand All @@ -125,6 +124,7 @@ export class AdvancedSearchComponent<T> implements OnInit {
EditorView.lineWrapping,
updateListener,
closeBrackets(),
// TODO: Extract placeholder into a property with a default value (or auto-build one based on the properties).
placeholder(this.translate.instant('Service = "SMB" AND Event = "CLOSE"')),
advancedSearchLinter,
diagnosticField,
Expand All @@ -142,13 +142,13 @@ export class AdvancedSearchComponent<T> implements OnInit {
}

dateSelected(value: string): void {
this.setEditorContents(`"${format(new Date(value), 'yyyy-MM-dd')}" `, this.editorView.state.doc.length);
this.appendEditorContents(`"${format(new Date(value), 'yyyy-MM-dd')}" `);
this.focusInput();
this.hideDatePicker();
}

protected onResetInput(): void {
this.setEditorContents('', 0, this.editorView.state.doc.length);
this.replaceEditorContents('');
this.focusInput();
this.hideDatePicker();
this.paramsChange.emit([]);
Expand All @@ -165,6 +165,7 @@ export class AdvancedSearchComponent<T> implements OnInit {

this.hasQueryErrors = Boolean(this.queryInputValue.length && parsedQuery.hasErrors);
this.cdr.markForCheck();
this.cdr.detectChanges();

if (parsedQuery.hasErrors && this.queryInputValue?.length) {
this.errorMessages = parsedQuery.errors;
Expand All @@ -173,22 +174,29 @@ export class AdvancedSearchComponent<T> implements OnInit {
parsedQuery.errors.filter((error) => error.from !== error.to),
),
});
} else {
this.editorView.dispatch({
effects: setDiagnostics.of([]),
});
this.errorMessages = null;
return;
}

const filters = this.queryToApi.buildFilters(parsedQuery, this.properties);
this.editorView.dispatch({
effects: setDiagnostics.of([]),
});
this.errorMessages = null;

const filters = this.queryToApi.buildFilters(parsedQuery, this.properties);
this.paramsChange.emit(filters);
}

private setEditorContents(contents: string, from = 0, to?: number): void {
private replaceEditorContents(contents: string): void {
this.editorView.dispatch({
changes: { from: 0, to: this.editorView.state.doc.length, insert: contents },
selection: { anchor: contents.length },
});
}

private appendEditorContents(contents: string): void {
this.editorView.dispatch({
changes: { from, to, insert: contents },
selection: { anchor: from + contents.length },
changes: { from: this.editorView.state.doc.length, insert: contents },
selection: { anchor: this.editorView.state.doc.length + contents.length },
});
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { ComponentHarness } from '@angular/cdk/testing';
import { EditorView } from '@codemirror/view';
import {
CodemirrorAutocompleteHarness,
} from 'app/modules/search-input/components/advanced-search/codemirror-autocomplete.harness';

export class AdvancedSearchHarness extends ComponentHarness {
static hostSelector = 'ix-advanced-search';
Expand All @@ -8,12 +10,7 @@ export class AdvancedSearchHarness extends ComponentHarness {
getInputArea = this.locatorFor('.cm-content');
getInputPlaceholder = this.locatorFor('.cm-placeholder');
getSwitchLink = this.locatorFor('.switch-link');

editor: EditorView;

setEditor(editor: EditorView): void {
this.editor = editor;
}
getAutocomplete = this.documentRootLocatorFactory().locatorFor(CodemirrorAutocompleteHarness);

async getValue(): Promise<string> {
return (await (this.getInputArea())).text();
Expand All @@ -25,16 +22,16 @@ export class AdvancedSearchHarness extends ComponentHarness {

async setValue(value: string): Promise<void> {
const inputArea = await this.getInputArea();
await inputArea.setContenteditableValue(value);

await inputArea.dispatchEvent('input');

if (this.editor) {
this.editor.dispatch({
changes: { from: 0, to: 0, insert: value },
});
} else {
await inputArea.setContenteditableValue(value);
}
// Using fakeAsync doesn't work for some reason.
await new Promise((resolve) => {
setTimeout(resolve);
});

return inputArea.dispatchEvent('input');
await this.forceStabilize();
}

async clickSwitchToBasic(): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ComponentHarness, parallel } from '@angular/cdk/testing';

export class CodemirrorAutocompleteHarness extends ComponentHarness {
static hostSelector = '.cm-tooltip-autocomplete';

private getOptionElements = this.locatorForAll('li');

async getOptions(): Promise<string[]> {
const items = await this.getOptionElements();
return parallel(() => items.map((item) => item.text()));
}

async select(text: string): Promise<void> {
const items = await this.getOptionElements();
let selectedItem = null;

for (const item of items) {
if ((await item.text()) === text) {
selectedItem = item;
break;
}
}

if (!selectedItem) {
throw new Error(`Cannot find item with text "${text}"`);
}

return selectedItem.click();
}
}
Loading

0 comments on commit 87a89f1

Please sign in to comment.