You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by se...@apache.org on 2021/01/25 06:39:18 UTC
[directory-studio] 01/01: Add tests against Fedora 389 DS
This is an automated email from the ASF dual-hosted git repository.
seelmann pushed a commit to branch add-fedora-389ds-test
in repository https://gitbox.apache.org/repos/asf/directory-studio.git
commit 08aa7750a79953fd47e771ccb25e51db7bcb28dc
Author: Stefan Seelmann <ma...@stefan-seelmann.de>
AuthorDate: Mon Jan 25 07:38:54 2021 +0100
Add tests against Fedora 389 DS
---
Jenkinsfile | 18 +-
.../test/integration/ui/Fedora389dsTest.java | 422 +++++++++++++++++++++
.../test/integration/ui/Fedora389dsTest.ldif | 226 +++++++++++
3 files changed, 660 insertions(+), 6 deletions(-)
diff --git a/Jenkinsfile b/Jenkinsfile
index feb0d07..ab87b5a 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -38,8 +38,10 @@ pipeline {
steps {
script {
docker.image('osixia/openldap:1.3.0').withRun() { openldap ->
- docker.image('apachedirectory/maven-build:jdk-8').inside("--link=${openldap.id}:openldap -e OPENLDAP_HOST=openldap -e OPENLDAP_PORT=389") {
- sh 'export DISPLAY=:99; env; ps aux'
+ docker.image('389ds/dirsrv').withRun('', 'bash -c "set -m; /usr/lib/dirsrv/dscontainer -r & sleep 30; /usr/sbin/dsconf localhost backend create --suffix dc=example,dc=org --be-name example; fg"') { fedora389ds ->
+ docker.image('apachedirectory/maven-build:jdk-8').inside("--link=${openldap.id}:openldap -e OPENLDAP_HOST=openldap -e OPENLDAP_PORT=389 --link=${fedora389ds.id}:fedora389ds -e FEDORA_389DS_HOST=fedora389ds -e FEDORA_389DS_PORT=3389") {
+ sh 'export DISPLAY=:99; env; ps aux'
+ }
}
}
}
@@ -63,8 +65,10 @@ pipeline {
steps {
script {
docker.image('osixia/openldap:1.3.0').withRun() { openldap ->
- docker.image('apachedirectory/maven-build:jdk-11').inside("--link=${openldap.id}:openldap -e OPENLDAP_HOST=openldap -e OPENLDAP_PORT=389") {
- sh 'export DISPLAY=:99; mvn -V -U -f pom-first.xml clean install && mvn -V clean install -Dorg.eclipse.swtbot.search.timeout=20000 -Denable-ui-tests'
+ docker.image('389ds/dirsrv').withRun('', 'bash -c "set -m; /usr/lib/dirsrv/dscontainer -r & sleep 30; /usr/sbin/dsconf localhost backend create --suffix dc=example,dc=org --be-name example; fg"') { fedora389ds ->
+ docker.image('apachedirectory/maven-build:jdk-11').inside("--link=${openldap.id}:openldap -e OPENLDAP_HOST=openldap -e OPENLDAP_PORT=389 --link=${fedora389ds.id}:fedora389ds -e FEDORA_389DS_HOST=fedora389ds -e FEDORA_389DS_PORT=3389") {
+ sh 'export DISPLAY=:99; mvn -V -U -f pom-first.xml clean install && mvn -V clean install -Dorg.eclipse.swtbot.search.timeout=20000 -Denable-ui-tests'
+ }
}
}
}
@@ -88,8 +92,10 @@ pipeline {
steps {
script {
docker.image('osixia/openldap:1.3.0').withRun() { openldap ->
- docker.image('apachedirectory/maven-build:jdk-15').inside("--link=${openldap.id}:openldap -e OPENLDAP_HOST=openldap -e OPENLDAP_PORT=389") {
- sh 'export DISPLAY=:99; mvn -V -U -f pom-first.xml clean install && mvn -V clean install -Dorg.eclipse.swtbot.search.timeout=20000 -Denable-ui-tests'
+ docker.image('389ds/dirsrv').withRun('', 'bash -c "set -m; /usr/lib/dirsrv/dscontainer -r & sleep 30; /usr/sbin/dsconf localhost backend create --suffix dc=example,dc=org --be-name example; fg"') { fedora389ds ->
+ docker.image('apachedirectory/maven-build:jdk-15').inside("--link=${openldap.id}:openldap -e OPENLDAP_HOST=openldap -e OPENLDAP_PORT=389 --link=${fedora389ds.id}:fedora389ds -e FEDORA_389DS_HOST=fedora389ds -e FEDORA_389DS_PORT=3389") {
+ sh 'export DISPLAY=:99; mvn -V -U -f pom-first.xml clean install && mvn -V clean install -Dorg.eclipse.swtbot.search.timeout=20000 -Denable-ui-tests'
+ }
}
}
}
diff --git a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/Fedora389dsTest.java b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/Fedora389dsTest.java
new file mode 100644
index 0000000..9eb525b
--- /dev/null
+++ b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/Fedora389dsTest.java
@@ -0,0 +1,422 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.directory.studio.test.integration.ui;
+
+
+import static org.apache.directory.studio.test.integration.ui.Constants.LOCALHOST;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.directory.api.ldap.model.exception.LdapAuthenticationException;
+import org.apache.directory.api.ldap.model.ldif.LdifEntry;
+import org.apache.directory.api.ldap.model.ldif.LdifReader;
+import org.apache.directory.ldap.client.api.LdapNetworkConnection;
+import org.apache.directory.ldap.client.api.exception.InvalidConnectionException;
+import org.apache.directory.studio.connection.core.Connection;
+import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection;
+import org.apache.directory.studio.test.integration.ui.bots.BrowserViewBot;
+import org.apache.directory.studio.test.integration.ui.bots.ConnectionsViewBot;
+import org.apache.directory.studio.test.integration.ui.bots.EntryEditorBot;
+import org.apache.directory.studio.test.integration.ui.bots.ModificationLogsViewBot;
+import org.apache.directory.studio.test.integration.ui.bots.NewAttributeWizardBot;
+import org.apache.directory.studio.test.integration.ui.bots.NewConnectionWizardBot;
+import org.apache.directory.studio.test.integration.ui.bots.SearchDialogBot;
+import org.apache.directory.studio.test.integration.ui.bots.StudioBot;
+import org.apache.directory.studio.test.integration.ui.bots.utils.Assertions;
+import org.apache.directory.studio.test.integration.ui.bots.utils.FrameworkRunnerWithScreenshotCaptureListener;
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/**
+ * Tests against 389 Directory Sever.
+ *
+ * Expects a running 389ds server with below default connection parameters
+ * which can be configured via environment variables.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+@RunWith(FrameworkRunnerWithScreenshotCaptureListener.class)
+public class Fedora389dsTest
+{
+ static String getOrDefault( String key, String defaultValue )
+ {
+ return System.getenv().getOrDefault( key, defaultValue );
+ }
+
+ private static final String FEDORA_389DS_HOST = getOrDefault( "FEDORA_389DS_HOST", LOCALHOST );
+ private static final int FEDORA_389DS_PORT = Integer.parseInt( getOrDefault( "FEDORA_389DS__PORT", "21389" ) );
+ private static final String FEDORA_389DS_ADMIN_DN = getOrDefault( "FEDORA_389DS_ADMIN_DN", "cn=Directory Manager" );
+ private static final String FEDORA_389DS_ADMIN_PASSWORD = getOrDefault( "FEDORA_389DS_ADMIN_PASSWORD", "admin" );
+
+ @BeforeClass
+ public static void skip389dsTestIfNotRunning() throws Exception
+ {
+
+ try ( LdapNetworkConnection connection = new LdapNetworkConnection( FEDORA_389DS_HOST, FEDORA_389DS_PORT ) )
+ {
+ connection.connect();
+ connection.bind( FEDORA_389DS_ADMIN_DN, FEDORA_389DS_ADMIN_PASSWORD );
+ }
+ catch ( InvalidConnectionException e )
+ {
+ Assume.assumeNoException(
+ "Skipping tests as connection to Fedora 389 DS failed: " + FEDORA_389DS_HOST + ":" + FEDORA_389DS_PORT, e );
+ }
+ catch ( LdapAuthenticationException e )
+ {
+ Assume.assumeNoException(
+ "Skipping tests as bind to Fedora 389 DS failed: " + FEDORA_389DS_HOST + ":" + FEDORA_389DS_PORT, e );
+ }
+ }
+
+ private StudioBot studioBot;
+ private ConnectionsViewBot connectionsViewBot;
+ private BrowserViewBot browserViewBot;
+ private ModificationLogsViewBot modificationLogsViewBot;
+
+ private Connection connection;
+
+ @Before
+ public void setUp() throws Exception
+ {
+ try ( LdapNetworkConnection connection = new LdapNetworkConnection( FEDORA_389DS_HOST, FEDORA_389DS_PORT );
+ LdifReader ldifReader = new LdifReader( Fedora389dsTest.class.getResourceAsStream( "Fedora389dsTest.ldif" ) ) )
+ {
+ connection.bind( FEDORA_389DS_ADMIN_DN, FEDORA_389DS_ADMIN_PASSWORD );
+ for ( LdifEntry entry : ldifReader )
+ {
+ connection.add( entry.getEntry() );
+ }
+ }
+
+ studioBot = new StudioBot();
+ studioBot.resetLdapPerspective();
+ connectionsViewBot = studioBot.getConnectionView();
+ connection = connectionsViewBot.createTestConnection( "Fedora389dsTest", FEDORA_389DS_HOST, FEDORA_389DS_PORT,
+ FEDORA_389DS_ADMIN_DN, FEDORA_389DS_ADMIN_PASSWORD );
+ browserViewBot = studioBot.getBrowserView();
+ modificationLogsViewBot = studioBot.getModificationLogsViewBot();
+ }
+
+
+ @After
+ public void tearDown() throws Exception
+ {
+ connectionsViewBot.deleteTestConnections();
+ Assertions.genericTearDownAssertions();
+
+ try ( LdapNetworkConnection connection = new LdapNetworkConnection( FEDORA_389DS_HOST, FEDORA_389DS_PORT );
+ LdifReader ldifReader = new LdifReader( Fedora389dsTest.class.getResourceAsStream( "Fedora389dsTest.ldif" ) ) )
+ {
+ connection.bind( FEDORA_389DS_ADMIN_DN, FEDORA_389DS_ADMIN_PASSWORD );
+ List<LdifEntry> ldifEntries = StreamSupport.stream( ldifReader.spliterator(), false )
+ .collect( Collectors.toList() );
+ Collections.reverse( ldifEntries );
+ for ( LdifEntry entry : ldifEntries )
+ {
+ connection.delete( entry.getDn() );
+ }
+ }
+ }
+
+
+ @Test
+ public void testBrowseWithPagingWithScrollMode()
+ {
+ browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users" );
+
+ // enable Simple Paged Results control
+ connection.getConnectionParameter().setExtendedBoolProperty(
+ IBrowserConnection.CONNECTION_PARAMETER_PAGED_SEARCH, true );
+ connection.getConnectionParameter().setExtendedIntProperty(
+ IBrowserConnection.CONNECTION_PARAMETER_PAGED_SEARCH_SIZE, 3 );
+ connection.getConnectionParameter().setExtendedBoolProperty(
+ IBrowserConnection.CONNECTION_PARAMETER_PAGED_SEARCH_SCROLL_MODE, true );
+
+ // 1st page
+ browserViewBot.expandEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users" );
+ assertFalse(
+ browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Top Page ---" ) );
+ assertTrue(
+ browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Next Page ---" ) );
+
+ // next page
+ browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Next Page ---" );
+ assertTrue(
+ browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Top Page ---" ) );
+ assertTrue(
+ browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Next Page ---" ) );
+
+ // last page
+ browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Next Page ---" );
+ assertTrue(
+ browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Top Page ---" ) );
+ assertFalse(
+ browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Next Page ---" ) );
+
+ // back to top
+ browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Top Page ---" );
+ assertFalse(
+ browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Top Page ---" ) );
+ assertTrue(
+ browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Next Page ---" ) );
+ }
+
+
+ @Test
+ public void testBrowseWithPagingWithoutScrollMode()
+ {
+ browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users" );
+
+ // enable Simple Paged Results control
+ connection.getConnectionParameter().setExtendedBoolProperty(
+ IBrowserConnection.CONNECTION_PARAMETER_PAGED_SEARCH, true );
+ connection.getConnectionParameter().setExtendedIntProperty(
+ IBrowserConnection.CONNECTION_PARAMETER_PAGED_SEARCH_SIZE, 3 );
+ connection.getConnectionParameter().setExtendedBoolProperty(
+ IBrowserConnection.CONNECTION_PARAMETER_PAGED_SEARCH_SCROLL_MODE, false );
+
+ browserViewBot.expandEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users" );
+ assertFalse(
+ browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Top Page ---" ) );
+ assertFalse(
+ browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "--- Next Page ---" ) );
+ assertTrue( browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users (8)" ) );
+ assertTrue( browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "uid=user.1" ) );
+ assertTrue( browserViewBot.existsEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "uid=user.8" ) );
+ }
+
+
+ @Test
+ public void testSearchWithPagingWithScrollMode() throws Exception
+ {
+ String searchName = "Paged search with scroll mode";
+ browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users" );
+ SearchDialogBot dialogBot = browserViewBot.openSearchDialog();
+ assertTrue( dialogBot.isVisible() );
+ dialogBot.setSearchName( searchName );
+ dialogBot.setFilter( "(objectClass=*)" );
+ dialogBot.setReturningAttributes( "objectClass,ou,cn,uid" );
+ dialogBot.setControlPagedSearch( true, 3, true );
+ dialogBot.clickSearchButton();
+
+ // 1st page
+ browserViewBot.expandEntry( "Searches", searchName );
+ assertFalse( browserViewBot.existsEntry( "Searches", searchName, "--- Top Page ---" ) );
+ assertTrue( browserViewBot.existsEntry( "Searches", searchName, "--- Next Page ---" ) );
+
+ // next page
+ browserViewBot.selectEntry( "Searches", searchName, "--- Next Page ---" );
+ assertTrue( browserViewBot.existsEntry( "Searches", searchName, "--- Top Page ---" ) );
+ assertTrue( browserViewBot.existsEntry( "Searches", searchName, "--- Next Page ---" ) );
+
+ // last page
+ browserViewBot.selectEntry( "Searches", searchName, "--- Next Page ---" );
+ assertTrue( browserViewBot.existsEntry( "Searches", searchName, "--- Top Page ---" ) );
+ assertFalse( browserViewBot.existsEntry( "Searches", searchName, "--- Next Page ---" ) );
+
+ // back to top
+ browserViewBot.selectEntry( "Searches", searchName, "--- Top Page ---" );
+ assertFalse( browserViewBot.existsEntry( "Searches", searchName, "--- Top Page ---" ) );
+ assertTrue( browserViewBot.existsEntry( "Searches", searchName, "--- Next Page ---" ) );
+ }
+
+
+ @Test
+ public void testSearchWithPagingWithoutScrollMode() throws Exception
+ {
+ String searchName = "Paged search without scroll mode";
+ browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users" );
+ SearchDialogBot dialogBot = browserViewBot.openSearchDialog();
+ assertTrue( dialogBot.isVisible() );
+ dialogBot.setSearchName( searchName );
+ dialogBot.setFilter( "(objectClass=*)" );
+ dialogBot.setReturningAttributes( "objectClass,ou,cn,uid" );
+ dialogBot.setControlPagedSearch( true, 3, false );
+ dialogBot.clickSearchButton();
+
+ browserViewBot.expandEntry( "Searches", searchName );
+ assertFalse( browserViewBot.existsEntry( "Searches", searchName, "--- Top Page ---" ) );
+ assertFalse( browserViewBot.existsEntry( "Searches", searchName, "--- Next Page ---" ) );
+ assertTrue( browserViewBot.existsEntry( "Searches", searchName + " (9+)" ) );
+ assertTrue( browserViewBot.existsEntry( "Searches", searchName, "uid=user.1" ) );
+ assertTrue( browserViewBot.existsEntry( "Searches", searchName, "uid=user.8" ) );
+ }
+
+
+ @Test
+ public void testCheckAuthenticationButtonNotOK()
+ {
+ // enter connection parameter
+ NewConnectionWizardBot wizardBot = connectionsViewBot.openNewConnectionWizard();
+ wizardBot.typeConnectionName( "Fedora 389 DS connection test not ok" );
+ wizardBot.typeHost( FEDORA_389DS_HOST );
+ wizardBot.typePort( FEDORA_389DS_PORT );
+ wizardBot.clickNextButton();
+
+ // enter incorrect authentication parameter
+ wizardBot.typeUser( FEDORA_389DS_ADMIN_DN );
+ wizardBot.typePassword( "wrongpassword" );
+
+ // click "Check Network Parameter" button
+ String result = wizardBot.clickCheckAuthenticationButton();
+ assertNotNull( "Expected Error", result );
+ assertThat( result, containsString( "[LDAP result code 49 - invalidCredentials]" ) );
+
+ wizardBot.clickCancelButton();
+ }
+
+
+ /**
+ * Test adding, editing and deleting of attributes in the entry editor.
+ */
+ @Test
+ public void testAddEditDeleteAttribute() throws Exception
+ {
+ browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "uid=user.1" );
+
+ EntryEditorBot entryEditorBot = studioBot.getEntryEditorBot( "uid=user.1,ou=users,dc=example,dc=org" );
+ entryEditorBot.activate();
+ String dn = entryEditorBot.getDnText();
+ assertEquals( "DN: uid=user.1,ou=users,dc=example,dc=org", dn );
+ assertEquals( 22, entryEditorBot.getAttributeValues().size() );
+ assertEquals( "", modificationLogsViewBot.getModificationLogsText() );
+
+ // add description attribute
+ entryEditorBot.activate();
+ NewAttributeWizardBot wizardBot = entryEditorBot.openNewAttributeWizard();
+ assertTrue( wizardBot.isVisible() );
+ wizardBot.typeAttributeType( "description" );
+ wizardBot.clickFinishButton();
+ entryEditorBot.typeValueAndFinish( "This is the 1st description." );
+ assertEquals( 23, entryEditorBot.getAttributeValues().size() );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 1st description." ) );
+ modificationLogsViewBot.waitForText( "add: description\ndescription: This is the 1st description." );
+
+ // add second value
+ entryEditorBot.activate();
+ entryEditorBot.addValue( "description" );
+ entryEditorBot.typeValueAndFinish( "This is the 2nd description." );
+ assertEquals( 24, entryEditorBot.getAttributeValues().size() );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 1st description." ) );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 2nd description." ) );
+ modificationLogsViewBot.waitForText( "add: description\ndescription: This is the 2nd description." );
+
+ // edit second value
+ entryEditorBot.editValue( "description", "This is the 2nd description." );
+ entryEditorBot.typeValueAndFinish( "This is the 3rd description." );
+ assertEquals( 24, entryEditorBot.getAttributeValues().size() );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 1st description." ) );
+ assertFalse( entryEditorBot.getAttributeValues().contains( "description: This is the 2nd description." ) );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 3rd description." ) );
+ modificationLogsViewBot.waitForText( "delete: description\ndescription: This is the 2nd description." );
+ modificationLogsViewBot.waitForText( "add: description\ndescription: This is the 3rd description." );
+
+ // delete second value
+ entryEditorBot.deleteValue( "description", "This is the 3rd description." );
+ assertEquals( 23, entryEditorBot.getAttributeValues().size() );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 1st description." ) );
+ assertFalse( entryEditorBot.getAttributeValues().contains( "description: This is the 3rd description." ) );
+ modificationLogsViewBot.waitForText( "delete: description\ndescription: This is the 3rd description." );
+
+ // edit 1st value
+ entryEditorBot.editValue( "description", "This is the 1st description." );
+ entryEditorBot.typeValueAndFinish( "This is the final description." );
+ assertEquals( 23, entryEditorBot.getAttributeValues().size() );
+ assertFalse( entryEditorBot.getAttributeValues().contains( "description: This is the 1st description." ) );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the final description." ) );
+ modificationLogsViewBot.waitForText( "delete: description\ndescription: This is the 1st description." );
+ modificationLogsViewBot.waitForText( "add: description\ndescription: This is the final description." );
+
+ // delete 1st value/attribute
+ entryEditorBot.deleteValue( "description", "This is the final description." );
+ assertEquals( 22, entryEditorBot.getAttributeValues().size() );
+ assertFalse( entryEditorBot.getAttributeValues().contains( "description: This is the final description." ) );
+ modificationLogsViewBot.waitForText( "delete: description\ndescription: This is the final description.\n-" );
+
+ assertEquals( "Expected 6 modifications.", 6,
+ StringUtils.countMatches( modificationLogsViewBot.getModificationLogsText(), "#!RESULT OK" ) );
+ }
+
+
+ /**
+ * Test adding, editing and deleting of attributes without equality matching rule in the entry editor.
+ */
+ @Test
+ public void testAddEditDeleteAttributeWithoutEqualityMatchingRule() throws Exception
+ {
+ browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "uid=user.1" );
+
+ EntryEditorBot entryEditorBot = studioBot.getEntryEditorBot( "uid=user.1,ou=users,dc=example,dc=org" );
+ entryEditorBot.activate();
+ String dn = entryEditorBot.getDnText();
+ assertEquals( "DN: uid=user.1,ou=users,dc=example,dc=org", dn );
+ assertEquals( 22, entryEditorBot.getAttributeValues().size() );
+ assertEquals( "", modificationLogsViewBot.getModificationLogsText() );
+
+ // add facsimileTelephoneNumber attribute
+ entryEditorBot.activate();
+ NewAttributeWizardBot wizardBot = entryEditorBot.openNewAttributeWizard();
+ assertTrue( wizardBot.isVisible() );
+ wizardBot.typeAttributeType( "facsimileTelephoneNumber" );
+ wizardBot.clickFinishButton();
+ entryEditorBot.typeValueAndFinish( "+1 234 567 890" );
+ assertEquals( 23, entryEditorBot.getAttributeValues().size() );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: +1 234 567 890" ) );
+ modificationLogsViewBot
+ .waitForText( "add: facsimileTelephoneNumber\nfacsimileTelephoneNumber: +1 234 567 890" );
+
+ // edit value
+ entryEditorBot.editValue( "facsimileTelephoneNumber", "+1 234 567 890" );
+ entryEditorBot.typeValueAndFinish( "000000000000" );
+ assertEquals( 23, entryEditorBot.getAttributeValues().size() );
+ assertFalse( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: +1 234 567 890" ) );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: 000000000000" ) );
+ modificationLogsViewBot.waitForText( "delete: facsimileTelephoneNumber\nfacsimileTelephoneNumber: +1 234 567 890" );
+ modificationLogsViewBot.waitForText( "add: facsimileTelephoneNumber\nfacsimileTelephoneNumber: 000000000000" );
+
+ // delete 1st value/attribute
+ entryEditorBot.deleteValue( "facsimileTelephoneNumber", "000000000000" );
+ assertEquals( 22, entryEditorBot.getAttributeValues().size() );
+ assertFalse( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: 000000000000" ) );
+ modificationLogsViewBot.waitForText( "delete: facsimileTelephoneNumber\nfacsimileTelephoneNumber: 000000000000\n-" );
+
+ assertEquals( "Expected 3 modifications.", 3,
+ StringUtils.countMatches( modificationLogsViewBot.getModificationLogsText(), "#!RESULT OK" ) );
+ }
+
+}
diff --git a/tests/test.integration.ui/src/main/resources/org/apache/directory/studio/test/integration/ui/Fedora389dsTest.ldif b/tests/test.integration.ui/src/main/resources/org/apache/directory/studio/test/integration/ui/Fedora389dsTest.ldif
new file mode 100644
index 0000000..96ae752
--- /dev/null
+++ b/tests/test.integration.ui/src/main/resources/org/apache/directory/studio/test/integration/ui/Fedora389dsTest.ldif
@@ -0,0 +1,226 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+dn: dc=example,dc=org
+objectClass: top
+objectClass: domain
+dc: example
+
+dn: ou=users,dc=example,dc=org
+objectClass: top
+objectClass: organizationalUnit
+ou: users
+
+dn: uid=user.1,ou=users,dc=example,dc=org
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+givenName: Aaccf
+sn: Amar
+cn: Aaccf Amar
+initials: AA
+uid: user.1
+mail: user.1@null
+userPassword: password
+telephoneNumber: 976-893-3312
+homePhone: 337-310-0727
+pager: 185-156-4071
+mobile: 626-188-0934
+employeeNumber: 1
+street: 27919 Broadway Street
+l: Tallahassee
+st: DE
+postalCode: 67698
+postalAddress: Aaccf Amar$27919 Broadway Street$Tallahassee, DE 67698
+roomNumber: 1388
+
+dn: uid=user.2,ou=users,dc=example,dc=org
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+givenName: Aaren
+sn: Atp
+cn: Aaren Atp
+initials: AA
+uid: user.2
+mail: user.2@null
+userPassword: password
+telephoneNumber: 147-953-0396
+homePhone: 174-439-5329
+pager: 617-443-8449
+mobile: 045-464-2512
+employeeNumber: 2
+street: 36109 Center Street
+l: Harlingen
+st: CO
+postalCode: 21733
+postalAddress: Aaren Atp$36109 Center Street$Harlingen, CO 21733
+description: This is the description for Aaren Atp.
+roomNumber: 1198
+
+dn: uid=user.3,ou=users,dc=example,dc=org
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+givenName: Aarika
+sn: Atpco
+cn: Aarika Atpco
+initials: AA
+uid: user.3
+mail: user.3@null
+userPassword: password
+telephoneNumber: 925-356-4943
+homePhone: 099-983-0308
+pager: 806-672-7363
+mobile: 425-493-8009
+employeeNumber: 3
+street: 14730 Fourteenth Street
+l: Evansville
+st: ND
+postalCode: 95526
+postalAddress: Aarika Atpco$14730 Fourteenth Street$Evansville, ND 95526
+description: This is the description for Aarika Atpco.
+roomNumber: 1135
+
+dn: uid=user.4,ou=users,dc=example,dc=org
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+givenName: Aaron
+sn: Atrc
+cn: Aaron Atrc
+initials: AA
+uid: user.4
+mail: user.4@null
+userPassword: password
+telephoneNumber: 696-981-0488
+homePhone: 854-401-1330
+pager: 389-028-9518
+mobile: 117-882-4912
+employeeNumber: 4
+street: 98495 Fifteenth Street
+l: Miami
+st: PA
+postalCode: 16887
+postalAddress: Aaron Atrc$98495 Fifteenth Street$Miami, PA 16887
+description: This is the description for Aaron Atrc.
+roomNumber: 1311
+
+dn: uid=user.5,ou=users,dc=example,dc=org
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+givenName: Aartjan
+sn: Aalders
+cn: Aartjan Aalders
+initials: AA
+uid: user.5
+mail: user.5@null
+userPassword: password
+telephoneNumber: 657-627-9753
+homePhone: 109-158-4533
+pager: 801-392-0010
+mobile: 763-973-0947
+employeeNumber: 5
+street: 52408 Ridge Street
+l: Omaha
+st: AR
+postalCode: 08015
+postalAddress: Aartjan Aalders$52408 Ridge Street$Omaha, AR 08015
+description: This is the description for Aartjan Aalders.
+roomNumber: 1090
+
+dn: uid=user.6,ou=users,dc=example,dc=org
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+givenName: Abagael
+sn: Aasen
+cn: Abagael Aasen
+initials: AA
+uid: user.6
+mail: user.6@null
+userPassword: password
+telephoneNumber: 600-556-9017
+homePhone: 543-731-7797
+pager: 349-743-6572
+mobile: 042-921-9717
+employeeNumber: 6
+street: 87514 Dogwood Street
+l: Bloomington
+st: ID
+postalCode: 56762
+postalAddress: Abagael Aasen$87514 Dogwood Street$Bloomington, ID 56762
+description: This is the description for Abagael Aasen.
+roomNumber: 1449
+
+dn: uid=user.7,ou=users,dc=example,dc=org
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+givenName: Abagail
+sn: Abadines
+cn: Abagail Abadines
+initials: AA
+uid: user.7
+mail: user.7@null
+userPassword: password
+telephoneNumber: 628-294-0713
+homePhone: 313-764-3970
+pager: 118-541-2978
+mobile: 111-360-9243
+employeeNumber: 7
+street: 60751 Main Street
+l: Macon
+st: MI
+postalCode: 38755
+postalAddress: Abagail Abadines$60751 Main Street$Macon, MI 38755
+description: This is the description for Abagail Abadines.
+roomNumber: 1312
+
+dn: uid=user.8,ou=users,dc=example,dc=org
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+givenName: Abahri
+sn: Abazari
+cn: Abahri Abazari
+initials: AA
+uid: user.8
+mail: user.8@null
+userPassword: password
+telephoneNumber: 760-373-5805
+homePhone: 856-861-3648
+pager: 271-218-0027
+mobile: 957-465-6928
+employeeNumber: 8
+street: 81023 River Street
+l: Monroe
+st: SD
+postalCode: 51865
+postalAddress: Abahri Abazari$81023 River Street$Monroe, SD 51865
+description: This is the description for Abahri Abazari.
+roomNumber: 1963
+