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
+