1+ /**
2+ * WordPress dependencies
3+ */
4+ const { test, expect } = require ( '@wordpress/e2e-test-utils-playwright' ) ;
5+
6+ const PLUGIN_SLUG = 'secure-custom-fields' ;
7+ const TEST_PLUGIN_SLUG = 'scf-test-plugin-get-repeater-field' ;
8+ const FIELD_GROUP_LABEL = 'Movie Repeater Details' ;
9+ const REPEATER_FIELD_LABEL = 'Actors' ;
10+
11+ test . describe ( 'Field Type > Repeater' , ( ) => {
12+ test . beforeAll ( async ( { requestUtils } ) => {
13+ await requestUtils . activatePlugin ( PLUGIN_SLUG ) ;
14+ await requestUtils . activatePlugin ( TEST_PLUGIN_SLUG ) ;
15+ } ) ;
16+
17+ test . afterAll ( async ( { requestUtils } ) => {
18+ await requestUtils . deactivatePlugin ( PLUGIN_SLUG ) ;
19+ await requestUtils . deactivatePlugin ( TEST_PLUGIN_SLUG ) ;
20+ await requestUtils . deleteAllPosts ( ) ;
21+ } ) ;
22+
23+ test . beforeEach ( async ( { page, admin } ) => {
24+ await deleteFieldGroups ( page , admin ) ;
25+ } ) ;
26+
27+ test ( 'should create a repeater field with subfields and verify it in admin' , async ( {
28+ page,
29+ admin,
30+ editor,
31+ requestUtils,
32+ } ) => {
33+ // Navigate to Field Groups and create new.
34+ await admin . visitAdminPage ( 'edit.php' , 'post_type=acf-field-group' ) ;
35+ const addNewButton = page . locator ( 'a.acf-btn:has-text("Add New")' ) ;
36+ await addNewButton . click ( ) ;
37+
38+ // Fill field group title.
39+ await page . waitForSelector ( '#title' ) ;
40+ await page . fill ( '#title' , FIELD_GROUP_LABEL ) ;
41+
42+ // Add repeater field
43+ const fieldType = page . locator (
44+ 'select[id^="acf_fields-field_"][id$="-type"]'
45+ ) ;
46+ await fieldType . selectOption ( 'repeater' ) ;
47+
48+ // Set repeater field label
49+ const fieldLabel = page . locator (
50+ 'input[id^="acf_fields-field_"][id$="-label"]'
51+ ) ;
52+ await fieldLabel . fill ( REPEATER_FIELD_LABEL ) ;
53+
54+ // Find and click the "Add Field" button inside the repeater's sub fields section
55+ const addSubFieldButton = page . locator ( '.acf-field-setting-sub_fields a.add-first-field' )
56+ await addSubFieldButton . click ( ) ;
57+
58+ // Wait for the subfield to be added and set properties
59+ const subFieldLabel = page . locator (
60+ '.acf-field-object.acf-field-object-text input.field-label'
61+ ) . last ( ) ;
62+ await subFieldLabel . waitFor ( ) ;
63+ await subFieldLabel . fill ( 'Actor Name' ) ;
64+
65+ // Add another subfield
66+ const addAnotherSubFieldButton = page . locator (
67+ '.acf-field-setting-sub_fields .acf-is-subfields a.add-field.acf-btn-secondary'
68+ ) ;
69+ await addAnotherSubFieldButton . click ( ) ;
70+
71+ // Wait for the second subfield to be added
72+ const secondSubFieldLabel = page . locator (
73+ '.acf-field-object.acf-field-object-text input.field-label'
74+ ) . last ( ) ;
75+ await secondSubFieldLabel . waitFor ( ) ;
76+ await secondSubFieldLabel . fill ( 'Actor Email' ) ;
77+
78+ // Change the second subfield type from text to email
79+ const secondSubFieldType = page . locator (
80+ '.acf-field-object'
81+ ) . last ( ) . locator ( 'select.field-type' ) ;
82+ await secondSubFieldType . selectOption ( 'email' ) ;
83+
84+ // Submit form
85+ const publishButton = page . locator (
86+ 'button.acf-btn.acf-publish[type="submit"]'
87+ ) ;
88+ await publishButton . click ( ) ;
89+
90+ // Verify success message.
91+ const successNotice = page . locator ( '.updated.notice' ) ;
92+ await expect ( successNotice ) . toBeVisible ( ) ;
93+ await expect ( successNotice ) . toContainText ( 'Field group published' ) ;
94+
95+ // Verify field group appears in the list.
96+ await admin . visitAdminPage ( 'edit.php' , 'post_type=acf-field-group' ) ;
97+ const fieldGroupRow = page . locator (
98+ `tr:has-text("${ FIELD_GROUP_LABEL } ")`
99+ ) ;
100+ await expect ( fieldGroupRow ) . toBeVisible ( ) ;
101+
102+ // Create a new post
103+ const post = await requestUtils . createPost ( {
104+ title : 'Movie with Actors' ,
105+ status : 'draft' ,
106+ showWelcomeGuide : false ,
107+ } ) ;
108+
109+ // Navigate to edit post page
110+ await admin . editPost ( post . id ) ;
111+
112+ // Add a repeater row - using the correct selector matching the actual DOM element
113+ const addRowButton = page . locator (
114+ 'a.acf-button.acf-repeater-add-row[data-event="add-row"]'
115+ ) ;
116+ await addRowButton . click ( ) ;
117+
118+ // Fill in the fields in the repeater
119+ const actorNameField = page . locator (
120+ '.acf-field[data-name="actor_name"] input[type="text"]'
121+ ) . first ( ) ;
122+ await actorNameField . fill ( 'Morgan Freeman' ) ;
123+
124+ const characterEmailField = page . locator (
125+ '.acf-field[data-name="actor_email"] input[type="email"]'
126+ ) . first ( ) ;
127+ await characterEmailField . fill ( 'red@shawshank.com' ) ;
128+
129+ // Add another row
130+ await addRowButton . click ( ) ;
131+
132+ // Fill in the second row
133+ const actorNameField2 = page . locator (
134+ '.acf-field[data-name="actor_name"] input[type="text"]'
135+ ) . nth ( 1 ) ;
136+ await actorNameField2 . fill ( 'Tim Robbins' ) ;
137+
138+ const characterEmailField2 = page . locator (
139+ '.acf-field[data-name="actor_email"] input[type="email"]'
140+ ) . nth ( 1 ) ;
141+ await characterEmailField2 . fill ( 'andy@shawshank.com' ) ;
142+
143+ // Verify the movie title is displayed
144+ const previewPage = await editor . openPreviewPage ( ) ;
145+
146+ // Check for repeater field output
147+ const actorsList = previewPage . locator ( '#scf-test-actors' ) ;
148+ await expect ( actorsList ) . toBeVisible ( ) ;
149+
150+ // Check the heading
151+ await expect ( actorsList . locator ( 'h3' ) ) . toContainText ( 'Movie Cast' ) ;
152+ // Verify the first actor's information
153+ const firstActorItem = actorsList . locator ( 'li' ) . nth ( 0 ) ;
154+ await expect ( firstActorItem . locator ( '.actor-name' ) ) . toContainText ( 'Actor: Morgan Freeman' ) ;
155+ await expect ( firstActorItem . locator ( '.actor-email' ) ) . toContainText ( 'Email: red@shawshank.com' ) ;
156+
157+ // Verify the second actor's information
158+ const secondActorItem = actorsList . locator ( 'li' ) . nth ( 1 ) ;
159+ await expect ( secondActorItem . locator ( '.actor-name' ) ) . toContainText ( 'Actor: Tim Robbins' ) ;
160+ await expect ( secondActorItem . locator ( '.actor-email' ) ) . toContainText ( 'Email: andy@shawshank.com' ) ;
161+
162+ // Close the preview tab
163+ await previewPage . close ( ) ;
164+ } ) ;
165+ } ) ;
166+
167+ /**
168+ * Helper function to delete the field group
169+ */
170+ async function deleteFieldGroups ( page , admin ) {
171+ await admin . visitAdminPage ( 'edit.php' , 'post_type=acf-field-group' ) ;
172+
173+ // Find and select the field group row
174+ const allFieldGroupsCheckbox = page . locator ( 'input#cb-select-all-1' ) ;
175+
176+ if ( await allFieldGroupsCheckbox . isVisible ( ) ) {
177+ await allFieldGroupsCheckbox . check ( ) ;
178+ // Use bulk actions to trash the field group
179+ await page . selectOption ( '#bulk-action-selector-bottom' , 'trash' ) ;
180+ await page . click ( '#doaction2' ) ;
181+
182+ // Verify deletion success message
183+ const deleteMessage = page . locator ( '.updated.notice' ) ;
184+ await expect ( deleteMessage ) . toBeVisible ( { timeout : 5000 } ) ;
185+ await expect ( deleteMessage ) . toContainText ( 'moved to the Trash' ) ;
186+
187+ await emptyTrash ( page , admin ) ;
188+ }
189+ }
190+
191+ /**
192+ * Helper function to empty trash
193+ */
194+ async function emptyTrash ( page , admin ) {
195+ await admin . visitAdminPage (
196+ 'edit.php' ,
197+ 'post_status=trash&post_type=acf-field-group'
198+ ) ;
199+ const emptyTrashButton = page . locator (
200+ '.tablenav.bottom input[name="delete_all"][value="Empty Trash"]'
201+ ) ;
202+ await emptyTrashButton . waitFor ( { state : 'visible' } ) ;
203+ await emptyTrashButton . click ( ) ;
204+
205+ // Verify success notice
206+ const successNotice = page . locator ( '.notice.updated p' ) ;
207+ await expect ( successNotice ) . toBeVisible ( ) ;
208+ await expect ( successNotice ) . toHaveText ( / p e r m a n e n t l y d e l e t e d / ) ;
209+ }
0 commit comments