-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* Integrate instructor request form FE with API * Remove redundant statement * Move URL regex const to backend const file * Fix import path * Move URL regex to FieldValidator * Add validators to match backend fields * Add error message box * Change submit button display when loading * Combine final action into subscribe * Add max length validators for institution and country * Fix lint errors * Add test cases to test submission * Add specific error messages for form validation * Remove home page URL field * Fix lint errors * Remove url regex from test * Update snap * Clean up test code * Remove comment about home page URL * Change canSubmit check to getter * Fix form submit button not re-enabling on error * Add name pattern validator to front-end * Fix snapshot
- Loading branch information
Showing
13 changed files
with
254 additions
and
115 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package teammates.ui.constants; | ||
|
||
import com.fasterxml.jackson.annotation.JsonValue; | ||
|
||
import teammates.common.util.FieldValidator; | ||
|
||
/** | ||
* Special constants used by the back-end. | ||
*/ | ||
public enum ApiStringConst { | ||
// CHECKSTYLE.OFF:JavadocVariable | ||
EMAIL_REGEX(escapeRegex(FieldValidator.REGEX_EMAIL)); | ||
// CHECKSTYLE.ON:JavadocVariable | ||
|
||
private final Object value; | ||
|
||
ApiStringConst(Object value) { | ||
this.value = value; | ||
} | ||
|
||
@JsonValue | ||
public Object getValue() { | ||
return value; | ||
} | ||
|
||
/** | ||
* Escape regex pattern strings to ensure the pattern remains valid when converted to JS. | ||
*/ | ||
private static String escapeRegex(String regexStr) { | ||
String escapedRegexStr = regexStr; | ||
// Double escape backslashes | ||
escapedRegexStr = escapedRegexStr.replace("\\", "\\\\"); | ||
// Replace possessive zero or more times quantifier *+ that the email pattern uses | ||
// with greedy zero or more times quantifier * | ||
// as possessive quantifiers are not supported in JavaScript | ||
escapedRegexStr = escapedRegexStr.replace("*+", "*"); | ||
return escapedRegexStr; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,16 +63,6 @@ exports[`RequestPageComponent should render correctly after form is submitted 1` | |
[email protected] | ||
</td> | ||
</tr> | ||
<tr> | ||
<th | ||
scope="row" | ||
> | ||
Home Page URL | ||
</th> | ||
<td> | ||
u.exampleu.edu/jsmith | ||
</td> | ||
</tr> | ||
<tr> | ||
<th | ||
scope="row" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,3 +20,7 @@ label.qn { | |
.red-font { | ||
color: red; | ||
} | ||
|
||
.error-box { | ||
margin: 1rem 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,34 @@ | ||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; | ||
import { ReactiveFormsModule } from '@angular/forms'; | ||
import { By } from '@angular/platform-browser'; | ||
import { first } from 'rxjs'; | ||
import { Observable, first } from 'rxjs'; | ||
import { InstructorRequestFormModel } from './instructor-request-form-model'; | ||
import { InstructorRequestFormComponent } from './instructor-request-form.component'; | ||
import { AccountService } from '../../../../services/account.service'; | ||
import { AccountCreateRequest } from '../../../../types/api-request'; | ||
|
||
describe('InstructorRequestFormComponent', () => { | ||
let component: InstructorRequestFormComponent; | ||
let fixture: ComponentFixture<InstructorRequestFormComponent>; | ||
let accountService: AccountService; | ||
const typicalModel: InstructorRequestFormModel = { | ||
name: 'John Doe', | ||
institution: 'Example Institution', | ||
country: 'Example Country', | ||
email: '[email protected]', | ||
homePage: 'xyz.example.edu/john', | ||
comments: '', | ||
}; | ||
const typicalCreateRequest: AccountCreateRequest = { | ||
instructorEmail: typicalModel.email, | ||
instructorName: typicalModel.name, | ||
instructorInstitution: `${typicalModel.institution}, ${typicalModel.country}`, | ||
}; | ||
|
||
const accountServiceStub: Partial<AccountService> = { | ||
createAccountRequest: () => new Observable((subscriber) => { | ||
subscriber.next(); | ||
}), | ||
}; | ||
|
||
/** | ||
* Fills in form fields with the given data. | ||
|
@@ -27,19 +40,24 @@ describe('InstructorRequestFormComponent', () => { | |
component.institution.setValue(data.institution); | ||
component.country.setValue(data.country); | ||
component.email.setValue(data.email); | ||
component.homePage.setValue(data.homePage); | ||
component.comments.setValue(data.comments); | ||
} | ||
|
||
beforeEach(() => { | ||
beforeEach(waitForAsync(() => { | ||
TestBed.configureTestingModule({ | ||
declarations: [InstructorRequestFormComponent], | ||
imports: [ReactiveFormsModule], | ||
}); | ||
providers: [{ provide: AccountService, useValue: accountServiceStub }], | ||
}) | ||
.compileComponents(); | ||
})); | ||
|
||
beforeEach(() => { | ||
fixture = TestBed.createComponent(InstructorRequestFormComponent); | ||
component = fixture.componentInstance; | ||
|
||
accountService = TestBed.inject(AccountService); | ||
fixture.detectChanges(); | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('should create', () => { | ||
|
@@ -50,17 +68,20 @@ describe('InstructorRequestFormComponent', () => { | |
expect(fixture).toMatchSnapshot(); | ||
}); | ||
|
||
it('should emit requestSubmissionEvent once when submit button is clicked', () => { | ||
jest.spyOn(component.requestSubmissionEvent, 'emit'); | ||
it('should run onSubmit() when submit button is clicked', () => { | ||
jest.spyOn(component, 'onSubmit'); | ||
|
||
fillFormWith(typicalModel); | ||
const submitButton = fixture.debugElement.query(By.css('#submit-button')); | ||
submitButton.nativeElement.click(); | ||
|
||
expect(component.requestSubmissionEvent.emit).toHaveBeenCalledTimes(1); | ||
expect(component.onSubmit).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('should emit requestSubmissionEvent with the correct data when form is submitted', () => { | ||
jest.spyOn(accountService, 'createAccountRequest').mockReturnValue( | ||
new Observable((subscriber) => { subscriber.next(); })); | ||
|
||
// Listen for emitted value | ||
let actualModel: InstructorRequestFormModel | null = null; | ||
component.requestSubmissionEvent.pipe(first()) | ||
|
@@ -74,7 +95,17 @@ describe('InstructorRequestFormComponent', () => { | |
expect(actualModel!.institution).toBe(typicalModel.institution); | ||
expect(actualModel!.country).toBe(typicalModel.country); | ||
expect(actualModel!.email).toBe(typicalModel.email); | ||
expect(actualModel!.homePage).toBe(typicalModel.homePage); | ||
expect(actualModel!.comments).toBe(typicalModel.comments); | ||
}); | ||
|
||
it('should send the correct request data when form is submitted', () => { | ||
jest.spyOn(accountService, 'createAccountRequest').mockReturnValue( | ||
new Observable((subscriber) => { subscriber.next(); })); | ||
|
||
fillFormWith(typicalModel); | ||
component.onSubmit(); | ||
|
||
expect(accountService.createAccountRequest).toHaveBeenCalledTimes(1); | ||
expect(accountService.createAccountRequest).toHaveBeenCalledWith(expect.objectContaining(typicalCreateRequest)); | ||
}); | ||
}); |
Oops, something went wrong.