diff --git a/scheduler/fedora_openqa_schedule/conf_test_suites.py b/scheduler/fedora_openqa_schedule/conf_test_suites.py
--- a/scheduler/fedora_openqa_schedule/conf_test_suites.py
+++ b/scheduler/fedora_openqa_schedule/conf_test_suites.py
@@ -361,6 +361,14 @@
         "env": "x86",
         "type": "Server",
     },
+    "QA:Testcase_FreeIPA_web_ui": {
+        "env": "Result",
+        "type": "Server",
+    },
+    "QA:Testcase_FreeIPA_password_change": {
+        "env": "Result",
+        "type": "Server",
+    },
     #        "": {
     #            "name": "", # optional, use when same testcase occurs on multiple rows with different link text
     #            "section": "", # optional, some result pages have no sections
@@ -377,6 +385,19 @@
 }
 
 TESTSUITES = {
+    # each entry in this dict is named for an openQA test suite, and
+    # represents the Wikitcms test cases that passed if that openQA
+    # test suite passed. The entries can be simple lists of test case
+    # names, or they can be dicts. In the dict form, the special dict
+    # entry named 'PASS' is a list of test cases that passed if the
+    # overall openQA job passed, as in the simple list form. The other
+    # dict entries are named for individual openQA test modules, and
+    # are lists of test cases that passed if the overall openQA job
+    # passed *and* that specific test module passed. This handles
+    # test suites like realmd_join_cockpit, which has an optional
+    # module that covers QA:Testcase_FreeIPA_web_ui ; if that module
+    # passes we want to report a pass for that test case, if the job
+    # passes but that module fails we do *not* want to report a pass.
     "install_default": [
         "QA:Testcase_Boot_default_install",
         "QA:Testcase_install_to_VirtIO",
@@ -690,11 +711,19 @@
     "server_cockpit_basic": [
         "QA:Testcase_Server_cockpit_basic",
     ],
-    "realmd_join_cockpit": [
-        "QA:Testcase_realmd_join_cockpit",
-        "QA:Testcase_FreeIPA_realmd_login",
-        "QA:Testcase_domain_client_authenticate",
-    ],
+    "realmd_join_cockpit": {
+        "PASS": [
+            "QA:Testcase_realmd_join_cockpit",
+            "QA:Testcase_FreeIPA_realmd_login",
+            "QA:Testcase_domain_client_authenticate",
+        ],
+        "freeipa_webui_postinstall": [
+            "QA:Testcase_FreeIPA_web_ui",
+        ],
+        "freeipa_password_change_postinstall": [
+            "QA:Testcase_FreeIPA_password_change",
+        ],
+    },
     "realmd_join_sssd": [
         "QA:Testcase_realmd_join_sssd",
         "QA:Testcase_FreeIPA_realmd_login",
diff --git a/scheduler/fedora_openqa_schedule/report.py b/scheduler/fedora_openqa_schedule/report.py
--- a/scheduler/fedora_openqa_schedule/report.py
+++ b/scheduler/fedora_openqa_schedule/report.py
@@ -106,13 +106,40 @@
                 # to report results for these
                 logger.debug("Job was run with extra params! Will not report")
                 continue
-            testsuite = job['settings']['TEST']
+            tsname = job['settings']['TEST']
             # There usually ought to be an entry in TESTSUITES for all
             # tests, but just in case someone messed up, let's be safe
-            if testsuite not in conf_test_suites.TESTSUITES:
-                logger.warning("No TESTSUITES entry found for test {0}!".format(testsuite))
+            if tsname not in conf_test_suites.TESTSUITES:
+                logger.warning("No TESTSUITES entry found for test {0}!".format(tsname))
                 continue
-            for testcase in conf_test_suites.TESTSUITES[testsuite]:
+            passed = []
+            testsuite = conf_test_suites.TESTSUITES[tsname]
+            # testsuite can be simply a list of test case names - in which case all those test
+            # cases are considered 'passed' if the openQA job overall 'passed' - or a dict of
+            # lists of test case names. In the dict form, there is one special key, 'PASS'; the
+            # 'PASS' list works just like the simple list form, all test cases in that list
+            # 'passed' if the openQA job overall passed. Any other keys are the names of
+            # individual openQA test modules, which should be included in the job as non-fatal
+            # modules; if that module passed within the job, the test cases in the list 'passed'.
+            try:
+                # this is the dict case. first take all test cases from the 'PASS' list...
+                passed.extend(testsuite.pop('PASS', []))
+                # now check each individual test module status
+                for (modname, cases) in testsuite.items():
+                    try:
+                        # find the matching test module in the job's list (note this
+                        # assumes we don't run the same module multiple times, which
+                        # is something upstream has recently started allowing...)
+                        module = [module for module in job['modules'] if module['name'] == modname][0]
+                    except IndexError:
+                        logger.warning("Did not find expected module %s in job data!", modname)
+                        continue
+                    if module.get('result', '') == 'passed':
+                        passed.extend(cases)
+            except TypeError:
+                # this is the simple list case.
+                passed = testsuite
+            for testcase in passed:
                 # each 'testsuite' is a list using testcase names to indicate which Wikitcms tests
                 # have passed if this job passes. Each testcase name is the name of a dict in the
                 # TESTCASES dict-of-dicts which more precisely identifies the 'test instance' (when

