2023-02-16 22:39:57 +00:00
import type { Page } from "@playwright/test" ;
import { expect } from "@playwright/test" ;
2022-07-14 12:40:53 +00:00
2023-02-16 22:39:57 +00:00
import type { Fixtures } from "@calcom/web/playwright/lib/fixtures" ;
import { test } from "@calcom/web/playwright/lib/fixtures" ;
2022-08-13 11:04:57 +00:00
2023-01-04 13:30:46 +00:00
function todo ( title : string ) {
// eslint-disable-next-line playwright/no-skipped-test, @typescript-eslint/no-empty-function
test . skip ( title , ( ) = > { } ) ;
2022-07-14 12:40:53 +00:00
}
2022-08-13 11:04:57 +00:00
test . describe ( "Routing Forms" , ( ) = > {
2022-09-02 19:00:41 +00:00
test . describe ( "Zero State Routing Forms" , ( ) = > {
test ( "should be able to add a new form and view it" , async ( { page } ) = > {
await page . waitForSelector ( '[data-testid="empty-screen"]' ) ;
2022-07-14 12:40:53 +00:00
2022-09-02 19:00:41 +00:00
const formId = await addForm ( page ) ;
2022-07-14 12:40:53 +00:00
2022-09-22 17:23:43 +00:00
await page . click ( '[href="/apps/routing-forms/forms"]' ) ;
2022-07-20 18:30:57 +00:00
2022-09-02 19:00:41 +00:00
await page . waitForSelector ( '[data-testid="routing-forms-list"]' ) ;
// Ensure that it's visible in forms list
expect ( await page . locator ( '[data-testid="routing-forms-list"] > li' ) . count ( ) ) . toBe ( 1 ) ;
2022-08-13 11:04:57 +00:00
2023-05-17 08:47:48 +00:00
await gotoRoutingLink ( { page , formId } ) ;
await expect ( page . locator ( "text=Test Form Name" ) ) . toBeVisible ( ) ;
2022-08-13 11:04:57 +00:00
2022-09-22 17:23:43 +00:00
await page . goto ( ` apps/routing-forms/route-builder/ ${ formId } ` ) ;
2023-05-17 08:47:48 +00:00
await disableForm ( page ) ;
await gotoRoutingLink ( { page , formId } ) ;
await expect ( page . locator ( "text=ERROR 404" ) ) . toBeVisible ( ) ;
2022-09-02 19:00:41 +00:00
} ) ;
2022-08-13 11:04:57 +00:00
2022-09-02 19:00:41 +00:00
test ( "should be able to edit the form" , async ( { page } ) = > {
2023-01-04 13:30:46 +00:00
const formId = await addForm ( page ) ;
2022-09-02 19:00:41 +00:00
const description = "Test Description" ;
2022-07-14 12:40:53 +00:00
2023-04-12 20:58:03 +00:00
const label = "Test Label" ;
const createdFields : Record < number , { label : string ; typeIndex : number } > = { } ;
2023-05-17 08:47:48 +00:00
const { fieldTypesList : types , fields } = await addAllTypesOfFieldsAndSaveForm ( formId , page , {
description ,
label ,
} ) ;
2022-09-02 19:00:41 +00:00
2023-03-15 22:01:04 +00:00
expect ( await page . inputValue ( ` [data-testid="description"] ` ) ) . toBe ( description ) ;
2023-04-12 20:58:03 +00:00
expect ( await page . locator ( '[data-testid="field"]' ) . count ( ) ) . toBe ( types . length ) ;
2023-01-04 13:30:46 +00:00
2023-05-17 08:47:48 +00:00
fields . forEach ( ( item , index ) = > {
createdFields [ index ] = { label : item.label , typeIndex : index } ;
2023-04-12 20:58:03 +00:00
} ) ;
2023-05-17 08:47:48 +00:00
2023-04-12 20:58:03 +00:00
await expectCurrentFormToHaveFields ( page , createdFields , types ) ;
2022-07-14 12:40:53 +00:00
2022-09-22 17:23:43 +00:00
await page . click ( '[href*="/apps/routing-forms/route-builder/"]' ) ;
2023-01-04 13:30:46 +00:00
await selectNewRoute ( page ) ;
2022-09-02 19:00:41 +00:00
await page . click ( '[data-testid="add-rule"]' ) ;
2023-01-04 13:30:46 +00:00
2023-04-12 20:58:03 +00:00
const options = Object . values ( createdFields ) . map ( ( item ) = > item . label ) ;
await verifyFieldOptionsInRule ( options , page ) ;
2023-01-04 13:30:46 +00:00
} ) ;
test . describe ( "F1<-F2 Relationship" , ( ) = > {
2023-05-17 08:47:48 +00:00
test ( "Create relationship by adding F1 as route.Editing F1 should update F2" , async ( { page } ) = > {
2023-01-04 13:30:46 +00:00
const form1Id = await addForm ( page , { name : "F1" } ) ;
const form2Id = await addForm ( page , { name : "F2" } ) ;
await addOneFieldAndDescriptionAndSaveForm ( form1Id , page , {
description : "Form 1 Description" ,
field : {
label : "F1 Field1" ,
typeIndex : 1 ,
} ,
} ) ;
const { types } = await addOneFieldAndDescriptionAndSaveForm ( form2Id , page , {
description : "Form 2 Description" ,
field : {
label : "F2 Field1" ,
//TODO: Maybe choose some other type and choose type by it's name and not index
typeIndex : 1 ,
} ,
} ) ;
// Add F1 as Router to F2
await page . goto ( ` /apps/routing-forms/route-builder/ ${ form2Id } ` ) ;
await selectNewRoute ( page , {
// It should be F1. TODO: Verify that it's F1
routeSelectNumber : 2 ,
} ) ;
await saveCurrentForm ( page ) ;
// Expect F1 fields to be available in F2
await page . goto ( ` /apps/routing-forms/form-edit/ ${ form2Id } ` ) ;
2023-01-07 15:50:28 +00:00
//FIXME: Figure out why this delay is required. Without it field count comes out to be 1 only
await new Promise ( ( resolve ) = > setTimeout ( resolve , 1000 ) ) ;
2023-01-04 13:30:46 +00:00
expect ( await page . locator ( '[data-testid="field"]' ) . count ( ) ) . toBe ( 2 ) ;
await expectCurrentFormToHaveFields ( page , { 1 : { label : "F1 Field1" , typeIndex : 1 } } , types ) ;
// Add 1 more field in F1
await addOneFieldAndDescriptionAndSaveForm ( form1Id , page , {
field : {
label : "F1 Field2" ,
typeIndex : 1 ,
} ,
} ) ;
await page . goto ( ` /apps/routing-forms/form-edit/ ${ form2Id } ` ) ;
2023-01-07 15:50:28 +00:00
//FIXME: Figure out why this delay is required. Without it field count comes out to be 1 only
await new Promise ( ( resolve ) = > setTimeout ( resolve , 1000 ) ) ;
2023-01-04 13:30:46 +00:00
expect ( await page . locator ( '[data-testid="field"]' ) . count ( ) ) . toBe ( 3 ) ;
await expectCurrentFormToHaveFields ( page , { 2 : { label : "F1 Field2" , typeIndex : 1 } } , types ) ;
} ) ;
todo ( "Create relationship by using duplicate with live connect" ) ;
} ) ;
2023-05-17 08:47:48 +00:00
test ( "should be able to submit a prefilled form with all types of fields" , async ( { page } ) = > {
const formId = await addForm ( page ) ;
await page . click ( '[href*="/apps/routing-forms/route-builder/"]' ) ;
await selectNewRoute ( page ) ;
await selectOption ( {
selector : {
selector : ".data-testid-select-routing-action" ,
nth : 0 ,
} ,
option : 2 ,
page ,
} ) ;
await page . fill ( "[name=externalRedirectUrl]" , "https://www.google.com" ) ;
await saveCurrentForm ( page ) ;
const { fields } = await addAllTypesOfFieldsAndSaveForm ( formId , page , {
description : "Description" ,
label : "Test Field" ,
} ) ;
const queryString =
"firstField=456&Test Field Number=456&Test Field Select=456&Test Field MultiSelect=456&Test Field MultiSelect=789&Test Field Phone=456&Test Field Email=456@example.com" ;
await gotoRoutingLink ( { page , queryString } ) ;
await page . fill ( '[data-testid="form-field-Test Field Long Text"]' , "manual-fill" ) ;
expect ( await page . locator ( ` [data-testid="form-field-firstField"] ` ) . inputValue ( ) ) . toBe ( "456" ) ;
expect ( await page . locator ( ` [data-testid="form-field-Test Field Number"] ` ) . inputValue ( ) ) . toBe ( "456" ) ;
// TODO: Verify select and multiselect has prefilled values.
// expect(await page.locator(`[data-testid="form-field-Test Field Select"]`).inputValue()).toBe("456");
// expect(await page.locator(`[data-testid="form-field-Test Field MultiSelect"]`).inputValue()).toBe("456");
expect ( await page . locator ( ` [data-testid="form-field-Test Field Phone"] ` ) . inputValue ( ) ) . toBe ( "456" ) ;
expect ( await page . locator ( ` [data-testid="form-field-Test Field Email"] ` ) . inputValue ( ) ) . toBe (
"456@example.com"
) ;
await page . click ( 'button[type="submit"]' ) ;
await page . waitForURL ( ( url ) = > {
return url . hostname . includes ( "google.com" ) ;
} ) ;
const url = new URL ( page . url ( ) ) ;
// Coming from the response filled by booker
expect ( url . searchParams . get ( "firstField" ) ) . toBe ( "456" ) ;
// All other params come from prefill URL
expect ( url . searchParams . get ( "Test Field Number" ) ) . toBe ( "456" ) ;
expect ( url . searchParams . get ( "Test Field Long Text" ) ) . toBe ( "manual-fill" ) ;
expect ( url . searchParams . get ( "Test Field Select" ) ) . toBe ( "456" ) ;
expect ( url . searchParams . getAll ( "Test Field MultiSelect" ) ) . toMatchObject ( [ "456" , "789" ] ) ;
expect ( url . searchParams . get ( "Test Field Phone" ) ) . toBe ( "456" ) ;
expect ( url . searchParams . get ( "Test Field Email" ) ) . toBe ( "456@example.com" ) ;
} ) ;
2023-01-04 13:30:46 +00:00
// TODO: How to install the app just once?
test . beforeEach ( async ( { page , users } ) = > {
2023-01-23 09:58:41 +00:00
const user = await users . create (
{ username : "routing-forms" } ,
{
hasTeam : true ,
}
) ;
2023-01-04 13:30:46 +00:00
await user . login ( ) ;
// Install app
await page . goto ( ` /apps/routing-forms ` ) ;
await page . click ( '[data-testid="install-app-button"]' ) ;
2023-05-02 16:58:39 +00:00
await page . waitForURL ( ( url ) = > url . pathname === ` /apps/routing-forms/forms ` ) ;
2023-01-04 13:30:46 +00:00
} ) ;
test . afterEach ( async ( { users } ) = > {
// This also delete forms on cascade
await users . deleteAll ( ) ;
2022-09-02 19:00:41 +00:00
} ) ;
2022-07-14 12:40:53 +00:00
} ) ;
2022-08-13 11:04:57 +00:00
2023-01-04 13:30:46 +00:00
todo ( "should be able to duplicate form" ) ;
2022-08-13 11:04:57 +00:00
test . describe ( "Seeded Routing Form " , ( ) = > {
2023-01-04 13:30:46 +00:00
test . afterEach ( async ( { users } ) = > {
// This also delete forms on cascade
await users . deleteAll ( ) ;
} ) ;
2022-09-02 19:00:41 +00:00
const createUserAndLoginAndInstallApp = async function ( {
users ,
page ,
} : {
users : Fixtures [ "users" ] ;
page : Page ;
} ) {
2023-01-23 09:58:41 +00:00
const user = await users . create (
{ username : "routing-forms" } ,
{ seedRoutingForms : true , hasTeam : true }
) ;
2022-09-02 19:00:41 +00:00
await user . login ( ) ;
// Install app
2022-09-22 17:23:43 +00:00
await page . goto ( ` /apps/routing-forms ` ) ;
2022-09-02 19:00:41 +00:00
await page . click ( '[data-testid="install-app-button"]' ) ;
2023-05-02 16:58:39 +00:00
await page . waitForURL ( ( url ) = > url . pathname === ` /apps/routing-forms/forms ` ) ;
2022-09-02 19:00:41 +00:00
return user ;
} ;
2022-11-11 09:57:44 +00:00
test ( "Routing Link - Reporting and CSV Download " , async ( { page , users } ) = > {
2022-09-02 19:00:41 +00:00
const user = await createUserAndLoginAndInstallApp ( { users , page } ) ;
const routingForm = user . routingForms [ 0 ] ;
2022-11-11 09:57:44 +00:00
test . setTimeout ( 120000 ) ;
2022-09-02 19:00:41 +00:00
// Fill form when you are logged out
await users . logout ( ) ;
2022-08-13 11:04:57 +00:00
2022-11-11 09:57:44 +00:00
await fillSeededForm ( page , routingForm . id ) ;
2022-09-02 19:00:41 +00:00
// Log back in to view form responses.
await user . login ( ) ;
2022-11-11 09:57:44 +00:00
await page . goto ( ` /apps/routing-forms/reporting/ ${ routingForm . id } ` ) ;
// Can't keep waiting forever. So, added a timeout of 5000ms
2023-05-05 16:19:10 +00:00
await page . waitForResponse ( ( response ) = > response . url ( ) . includes ( "appRoutingForms/report" ) , {
2022-11-11 09:57:44 +00:00
timeout : 5000 ,
} ) ;
const headerEls = page . locator ( "[data-testid='reporting-header'] th" ) ;
// Once the response is there, React would soon render it, so 500ms is enough
2023-01-17 16:36:06 +00:00
// FIXME: Sometimes it takes more than 500ms, so added a timeout of 1000ms for now. There might be something wrong with rendering.
2022-11-11 09:57:44 +00:00
await headerEls . first ( ) . waitFor ( {
2023-01-17 16:36:06 +00:00
timeout : 1000 ,
2022-11-11 09:57:44 +00:00
} ) ;
const numHeaderEls = await headerEls . count ( ) ;
const headers = [ ] ;
for ( let i = 0 ; i < numHeaderEls ; i ++ ) {
headers . push ( await headerEls . nth ( i ) . innerText ( ) ) ;
}
const responses = [ ] ;
const responseRows = page . locator ( "[data-testid='reporting-row']" ) ;
const numResponseRows = await responseRows . count ( ) ;
for ( let i = 0 ; i < numResponseRows ; i ++ ) {
const rowLocator = responseRows . nth ( i ) . locator ( "td" ) ;
const numRowEls = await rowLocator . count ( ) ;
const rowResponses = [ ] ;
for ( let j = 0 ; j < numRowEls ; j ++ ) {
rowResponses . push ( await rowLocator . nth ( j ) . innerText ( ) ) ;
}
responses . push ( rowResponses ) ;
}
expect ( headers ) . toEqual ( [ "Test field" , "Multi Select" ] ) ;
expect ( responses ) . toEqual ( [
[ "event-routing" , "" ] ,
[ "external-redirect" , "" ] ,
[ "custom-page" , "" ] ,
] ) ;
2022-08-13 11:04:57 +00:00
const [ download ] = await Promise . all ( [
// Start waiting for the download
page . waitForEvent ( "download" ) ,
// Perform the action that initiates download
page . click ( '[data-testid="download-responses"]' ) ,
] ) ;
const downloadStream = await download . createReadStream ( ) ;
2022-09-02 19:00:41 +00:00
expect ( download . suggestedFilename ( ) ) . toEqual ( ` ${ routingForm . name } - ${ routingForm . id } .csv ` ) ;
2022-08-13 11:04:57 +00:00
const csv : string = await new Promise ( ( resolve ) = > {
let body = "" ;
downloadStream ? . on ( "data" , ( chunk ) = > {
body += chunk ;
} ) ;
downloadStream ? . on ( "end" , ( ) = > {
resolve ( body ) ;
} ) ;
} ) ;
2022-11-08 14:21:53 +00:00
const csvRows = csv . trim ( ) . split ( "\n" ) ;
const csvHeaderRow = csvRows [ 0 ] ;
expect ( csvHeaderRow ) . toEqual ( "Test field,Multi Select,Submission Time" ) ;
2022-08-13 11:04:57 +00:00
2022-11-08 14:21:53 +00:00
const firstResponseCells = csvRows [ 1 ] . split ( "," ) ;
const secondResponseCells = csvRows [ 2 ] . split ( "," ) ;
const thirdResponseCells = csvRows [ 3 ] . split ( "," ) ;
expect ( firstResponseCells . slice ( 0 , - 1 ) . join ( "," ) ) . toEqual ( "event-routing," ) ;
2023-03-02 18:58:31 +00:00
expect ( new Date ( firstResponseCells . at ( - 1 ) as string ) . getDay ( ) ) . toEqual ( new Date ( ) . getDay ( ) ) ;
2022-11-08 14:21:53 +00:00
expect ( secondResponseCells . slice ( 0 , - 1 ) . join ( "," ) ) . toEqual ( "external-redirect," ) ;
2023-03-02 18:58:31 +00:00
expect ( new Date ( secondResponseCells . at ( - 1 ) as string ) . getDay ( ) ) . toEqual ( new Date ( ) . getDay ( ) ) ;
2022-11-08 14:21:53 +00:00
expect ( thirdResponseCells . slice ( 0 , - 1 ) . join ( "," ) ) . toEqual ( "custom-page," ) ;
2023-03-02 18:58:31 +00:00
expect ( new Date ( thirdResponseCells . at ( - 1 ) as string ) . getDay ( ) ) . toEqual ( new Date ( ) . getDay ( ) ) ;
2022-08-13 11:04:57 +00:00
} ) ;
2022-09-02 19:00:41 +00:00
test ( "Router URL should work" , async ( { page , users } ) = > {
const user = await createUserAndLoginAndInstallApp ( { users , page } ) ;
const routingForm = user . routingForms [ 0 ] ;
// Router should be publicly accessible
await users . logout ( ) ;
page . goto ( ` /router?form= ${ routingForm . id } &Test field=event-routing ` ) ;
2023-05-02 16:58:39 +00:00
await page . waitForURL ( ( url ) = > {
return url . pathname . endsWith ( "/pro/30min" ) ;
2022-08-13 11:04:57 +00:00
} ) ;
2022-09-02 19:00:41 +00:00
page . goto ( ` /router?form= ${ routingForm . id } &Test field=external-redirect ` ) ;
2023-05-02 16:58:39 +00:00
await page . waitForURL ( ( url ) = > {
return url . hostname . includes ( "google.com" ) ;
2022-08-13 11:04:57 +00:00
} ) ;
2022-09-02 19:00:41 +00:00
await page . goto ( ` /router?form= ${ routingForm . id } &Test field=custom-page ` ) ;
2023-05-17 08:47:48 +00:00
await expect ( page . locator ( "text=Custom Page Result" ) ) . toBeVisible ( ) ;
2022-08-13 11:04:57 +00:00
2022-09-02 19:00:41 +00:00
await page . goto ( ` /router?form= ${ routingForm . id } &Test field=doesntmatter&multi=Option-2 ` ) ;
2023-05-17 08:47:48 +00:00
await expect ( page . locator ( "text=Multiselect chosen" ) ) . toBeVisible ( ) ;
2022-08-13 11:04:57 +00:00
} ) ;
2022-09-02 19:00:41 +00:00
test ( "Routing Link should validate fields" , async ( { page , users } ) = > {
const user = await createUserAndLoginAndInstallApp ( { users , page } ) ;
const routingForm = user . routingForms [ 0 ] ;
2023-05-17 08:47:48 +00:00
await gotoRoutingLink ( { page , formId : routingForm.id } ) ;
2022-08-13 11:04:57 +00:00
page . click ( 'button[type="submit"]' ) ;
const firstInputMissingValue = await page . evaluate ( ( ) = > {
return document . querySelectorAll ( "input" ) [ 0 ] . validity . valueMissing ;
} ) ;
expect ( firstInputMissingValue ) . toBe ( true ) ;
expect ( await page . locator ( 'button[type="submit"][disabled]' ) . count ( ) ) . toBe ( 0 ) ;
} ) ;
2022-11-14 08:50:09 +00:00
test ( "Test preview should return correct route" , async ( { page , users } ) = > {
const user = await createUserAndLoginAndInstallApp ( { users , page } ) ;
const routingForm = user . routingForms [ 0 ] ;
page . goto ( ` apps/routing-forms/form-edit/ ${ routingForm . id } ` ) ;
await page . click ( '[data-testid="test-preview"]' ) ;
// //event redirect
2023-05-17 08:47:48 +00:00
await page . fill ( '[data-testid="form-field-Test field"]' , "event-routing" ) ;
2022-11-14 08:50:09 +00:00
await page . click ( '[data-testid="test-routing"]' ) ;
let routingType = await page . locator ( '[data-testid="test-routing-result-type"]' ) . innerText ( ) ;
let route = await page . locator ( '[data-testid="test-routing-result"]' ) . innerText ( ) ;
2023-05-17 08:47:48 +00:00
expect ( routingType ) . toBe ( "Event Redirect" ) ;
expect ( route ) . toBe ( "pro/30min" ) ;
2022-11-14 08:50:09 +00:00
//custom page
2023-05-17 08:47:48 +00:00
await page . fill ( '[data-testid="form-field-Test field"]' , "custom-page" ) ;
2022-11-14 08:50:09 +00:00
await page . click ( '[data-testid="test-routing"]' ) ;
routingType = await page . locator ( '[data-testid="test-routing-result-type"]' ) . innerText ( ) ;
route = await page . locator ( '[data-testid="test-routing-result"]' ) . innerText ( ) ;
2023-05-17 08:47:48 +00:00
expect ( routingType ) . toBe ( "Custom Page" ) ;
expect ( route ) . toBe ( "Custom Page Result" ) ;
2022-11-14 08:50:09 +00:00
//external redirect
2023-05-17 08:47:48 +00:00
await page . fill ( '[data-testid="form-field-Test field"]' , "external-redirect" ) ;
2022-11-14 08:50:09 +00:00
await page . click ( '[data-testid="test-routing"]' ) ;
routingType = await page . locator ( '[data-testid="test-routing-result-type"]' ) . innerText ( ) ;
route = await page . locator ( '[data-testid="test-routing-result"]' ) . innerText ( ) ;
2023-05-17 08:47:48 +00:00
expect ( routingType ) . toBe ( "External Redirect" ) ;
expect ( route ) . toBe ( "https://google.com" ) ;
2022-11-14 08:50:09 +00:00
//fallback route
2023-05-17 08:47:48 +00:00
await page . fill ( '[data-testid="form-field-Test field"]' , "fallback" ) ;
2022-11-14 08:50:09 +00:00
await page . click ( '[data-testid="test-routing"]' ) ;
routingType = await page . locator ( '[data-testid="test-routing-result-type"]' ) . innerText ( ) ;
route = await page . locator ( '[data-testid="test-routing-result"]' ) . innerText ( ) ;
2023-05-17 08:47:48 +00:00
expect ( routingType ) . toBe ( "Custom Page" ) ;
expect ( route ) . toBe ( "Fallback Message" ) ;
2022-11-14 08:50:09 +00:00
} ) ;
2022-08-13 11:04:57 +00:00
} ) ;
2022-07-14 12:40:53 +00:00
} ) ;
2023-01-04 13:30:46 +00:00
2023-05-17 08:47:48 +00:00
async function disableForm ( page : Page ) {
await page . click ( '[data-testid="toggle-form"] [value="on"]' ) ;
await page . waitForSelector ( ".data-testid-toast-success" ) ;
}
2023-01-04 13:30:46 +00:00
async function expectCurrentFormToHaveFields (
page : Page ,
fields : {
[ key : number ] : { label : string ; typeIndex : number } ;
} ,
types : string [ ]
) {
for ( const [ index , field ] of Object . entries ( fields ) ) {
expect ( await page . inputValue ( ` [name="fields. ${ index } .label"] ` ) ) . toBe ( field . label ) ;
2023-02-16 22:39:57 +00:00
expect ( await page . locator ( ".data-testid-field-type" ) . nth ( + index ) . locator ( "div" ) . nth ( 1 ) . innerText ( ) ) . toBe (
types [ field . typeIndex ]
) ;
2023-01-04 13:30:46 +00:00
}
}
2022-11-11 09:57:44 +00:00
async function fillSeededForm ( page : Page , routingFormId : string ) {
2023-05-17 08:47:48 +00:00
await gotoRoutingLink ( { page , formId : routingFormId } ) ;
await page . fill ( '[data-testid="form-field-Test field"]' , "event-routing" ) ;
2022-11-11 09:57:44 +00:00
page . click ( 'button[type="submit"]' ) ;
2023-05-02 16:58:39 +00:00
await page . waitForURL ( ( url ) = > {
return url . pathname . endsWith ( "/pro/30min" ) ;
2022-11-11 09:57:44 +00:00
} ) ;
2023-05-17 08:47:48 +00:00
await gotoRoutingLink ( { page , formId : routingFormId } ) ;
await page . fill ( '[data-testid="form-field-Test field"]' , "external-redirect" ) ;
2022-11-11 09:57:44 +00:00
page . click ( 'button[type="submit"]' ) ;
2023-05-02 16:58:39 +00:00
await page . waitForURL ( ( url ) = > {
return url . hostname . includes ( "google.com" ) ;
2022-11-11 09:57:44 +00:00
} ) ;
2023-05-17 08:47:48 +00:00
await gotoRoutingLink ( { page , formId : routingFormId } ) ;
await page . fill ( '[data-testid="form-field-Test field"]' , "custom-page" ) ;
2022-11-11 09:57:44 +00:00
await page . click ( 'button[type="submit"]' ) ;
2023-05-17 08:47:48 +00:00
await expect ( page . locator ( "text=Custom Page Result" ) ) . toBeVisible ( ) ;
2022-11-11 09:57:44 +00:00
}
2023-01-04 13:30:46 +00:00
export async function addForm ( page : Page , { name = "Test Form Name" } = { } ) {
await page . goto ( "/apps/routing-forms/forms" ) ;
await page . click ( '[data-testid="new-routing-form"]' ) ;
await page . fill ( "input[name]" , name ) ;
await page . click ( '[data-testid="add-form"]' ) ;
await page . waitForSelector ( '[data-testid="add-field"]' ) ;
const url = page . url ( ) ;
const formId = new URL ( url ) . pathname . split ( "/" ) . at ( - 1 ) ;
if ( ! formId ) {
throw new Error ( "Form ID couldn't be determined from url" ) ;
}
return formId ;
}
2023-05-17 08:47:48 +00:00
async function addAllTypesOfFieldsAndSaveForm (
2023-04-12 20:58:03 +00:00
formId : string ,
page : Page ,
form : { description : string ; label : string }
) {
await page . goto ( ` apps/routing-forms/form-edit/ ${ formId } ` ) ;
await page . click ( '[data-testid="add-field"]' ) ;
await page . fill ( '[data-testid="description"]' , form . description ) ;
2023-05-17 08:47:48 +00:00
const { optionsInUi : fieldTypesList } = await verifySelectOptions (
2023-04-12 20:58:03 +00:00
{ selector : ".data-testid-field-type" , nth : 0 } ,
[ "Email" , "Long Text" , "MultiSelect" , "Number" , "Phone" , "Select" , "Short Text" ] ,
page
) ;
2023-05-17 08:47:48 +00:00
const fields = [ ] ;
for ( let index = 0 ; index < fieldTypesList . length ; index ++ ) {
const fieldTypeLabel = fieldTypesList [ index ] ;
const nth = index ;
const label = ` ${ form . label } ${ fieldTypeLabel } ` ;
let identifier = "" ;
if ( index !== 0 ) {
identifier = label ;
// Click on the field type dropdown.
await page . locator ( ".data-testid-field-type" ) . nth ( nth ) . click ( ) ;
// Click on the dropdown option.
await page . locator ( ` [data-testid="select-option- ${ fieldTypeLabel } "] ` ) . click ( ) ;
} else {
// Set the identifier manually for the first field to test out a case when identifier isn't computed from label automatically
// First field type is by default selected. So, no need to choose from dropdown
identifier = "firstField" ;
}
2023-04-12 20:58:03 +00:00
2023-05-17 08:47:48 +00:00
if ( fieldTypeLabel === "MultiSelect" || fieldTypeLabel === "Select" ) {
await page . fill ( ` [name="fields. ${ nth } .selectText"] ` , "123\n456\n789" ) ;
}
2023-04-12 20:58:03 +00:00
await page . fill ( ` [name="fields. ${ nth } .label"] ` , label ) ;
2023-05-17 08:47:48 +00:00
if ( identifier !== label ) {
await page . fill ( ` [name="fields. ${ nth } .identifier"] ` , identifier ) ;
}
if ( index !== fieldTypesList . length - 1 ) {
2023-04-12 20:58:03 +00:00
await page . click ( '[data-testid="add-field"]' ) ;
}
2023-05-17 08:47:48 +00:00
fields . push ( { identifier : identifier , label , type : fieldTypeLabel } ) ;
2023-04-12 20:58:03 +00:00
}
await saveCurrentForm ( page ) ;
return {
2023-05-17 08:47:48 +00:00
fieldTypesList ,
fields ,
2023-04-12 20:58:03 +00:00
} ;
}
2023-01-04 13:30:46 +00:00
export async function addOneFieldAndDescriptionAndSaveForm (
formId : string ,
page : Page ,
form : { description? : string ; field ? : { typeIndex : number ; label : string } }
) {
await page . goto ( ` apps/routing-forms/form-edit/ ${ formId } ` ) ;
await page . click ( '[data-testid="add-field"]' ) ;
if ( form . description ) {
await page . fill ( '[data-testid="description"]' , form . description ) ;
}
// Verify all Options of SelectBox
const { optionsInUi : types } = await verifySelectOptions (
{ selector : ".data-testid-field-type" , nth : 0 } ,
[ "Email" , "Long Text" , "MultiSelect" , "Number" , "Phone" , "Select" , "Short Text" ] ,
page
) ;
const nextFieldIndex = ( await page . locator ( '[data-testid="field"]' ) . count ( ) ) - 1 ;
if ( form . field ) {
await page . fill ( ` [name="fields. ${ nextFieldIndex } .label"] ` , form . field . label ) ;
await page
. locator ( '[data-testid="field"]' )
. nth ( nextFieldIndex )
. locator ( ".data-testid-field-type" )
. click ( ) ;
await page
. locator ( '[data-testid="field"]' )
. nth ( nextFieldIndex )
. locator ( '[id*="react-select-"][aria-disabled]' )
. nth ( form . field . typeIndex )
. click ( ) ;
}
await saveCurrentForm ( page ) ;
return {
types ,
} ;
}
async function selectOption ( {
page ,
selector ,
option ,
} : {
page : Page ;
selector : { selector : string ; nth : number } ;
2023-05-17 08:47:48 +00:00
/ * *
* Index of option to select . Starts from 1
* /
2023-01-04 13:30:46 +00:00
option : number ;
} ) {
const locatorForSelect = page . locator ( selector . selector ) . nth ( selector . nth ) ;
await locatorForSelect . click ( ) ;
await locatorForSelect
. locator ( '[id*="react-select-"][aria-disabled]' )
. nth ( option - 1 )
. click ( ) ;
}
async function verifyFieldOptionsInRule ( options : string [ ] , page : Page ) {
await verifySelectOptions (
{
selector : ".rule-container .data-testid-field-select" ,
nth : 0 ,
} ,
options ,
page
) ;
}
async function verifySelectOptions (
selector : { selector : string ; nth : number } ,
expectedOptions : string [ ] ,
page : Page
) {
await page . locator ( selector . selector ) . nth ( selector . nth ) . click ( ) ;
const selectOptions = await page
. locator ( selector . selector )
. nth ( selector . nth )
. locator ( '[id*="react-select-"][aria-disabled]' )
. allInnerTexts ( ) ;
const sortedSelectOptions = [ . . . selectOptions ] . sort ( ) ;
const sortedExpectedOptions = [ . . . expectedOptions ] . sort ( ) ;
expect ( sortedSelectOptions ) . toEqual ( sortedExpectedOptions ) ;
return {
optionsInUi : selectOptions ,
} ;
}
async function selectNewRoute ( page : Page , { routeSelectNumber = 1 } = { } ) {
await selectOption ( {
selector : {
selector : ".data-testid-select-router" ,
nth : 0 ,
} ,
option : routeSelectNumber ,
page ,
} ) ;
}
2023-05-17 08:47:48 +00:00
async function gotoRoutingLink ( {
page ,
formId ,
queryString = "" ,
} : {
page : Page ;
formId? : string ;
queryString? : string ;
} ) {
let previewLink = null ;
if ( ! formId ) {
// Instead of clicking on the preview link, we are going to the preview link directly because the earlier opens a new tab which is a bit difficult to manage with Playwright
const href = await page . locator ( '[data-testid="form-action-preview"]' ) . getAttribute ( "href" ) ;
if ( ! href ) {
throw new Error ( "Preview link not found" ) ;
}
previewLink = href ;
} else {
previewLink = ` /forms/ ${ formId } ` ;
}
await page . goto ( ` ${ previewLink } ${ queryString ? ` ? ${ queryString } ` : "" } ` ) ;
2023-01-04 13:30:46 +00:00
// HACK: There seems to be some issue with the inputs to the form getting reset if we don't wait.
await new Promise ( ( resolve ) = > setTimeout ( resolve , 500 ) ) ;
}
async function saveCurrentForm ( page : Page ) {
await page . click ( '[data-testid="update-form"]' ) ;
await page . waitForSelector ( ".data-testid-toast-success" ) ;
}