#!/usr/bin/python3
# Run this with --help to see available options for tracing and debugging
# See https://github.com/cockpit-project/cockpit/blob/master/test/common/testlib.py
# "class Browser" and "class MachineCase" for the available API.

import os
import sys
import time
from distutils.version import StrictVersion
import unittest

# import Cockpit's machinery for test VMs and its browser test API
TEST_DIR = os.path.dirname(__file__)
sys.path.append(os.path.join(TEST_DIR, "common"))
sys.path.append(os.path.join(os.path.dirname(TEST_DIR), "bots/machine"))
import testlib

REGISTRIES_CONF="""
[registries.search]
registries = ['localhost:5000']

[registries.insecure]
registries = ['localhost:5000']
"""

class TestApplication(testlib.MachineCase):

    def testBasic(self):
        b = self.browser
        m = self.machine

        self.login_and_go("/podman")
        b.wait_present("#app")
        b.wait_present(".content-filter div")
        b.wait_in_text("#containers-images", "busybox:latest")

        # show image listing toggle
        b.wait_present('#containers-images tr:contains("busybox:latest")')
        b.click('#containers-images tbody tr:contains("busybox:latest") td.listing-ct-toggle')
        b.wait_present('#containers-images tbody tr:contains("busybox:latest") + tr button.btn-delete')
        b.wait_in_text('#containers-images tbody tr .listing-ct-body:first-child:contains("busybox:latest")', "Commandsh")

        # make sure no running containers shown
        self.filter_containers('running')
        b.wait_in_text("#containers-containers", "No running containers")

        # run a container (will exit immediately)
        m.execute("podman run -d --name test-sh alpine sh")
        # run a container
        m.execute("podman run -d --name swamped-crate busybox sleep 1000")

        # running busybox shown
        b.wait_present("#containers-containers")
        b.wait_present('#containers-containers tr:contains("swamped-crate")')
        self.check_container('swamped-crate', ['swamped-crate', 'busybox:latest', 'sleep 1000', 'running'])

        # exited alpine not shown
        b.wait_not_in_text("#containers-containers", "alpine:latest")

        # show all containers and check status
        self.filter_containers('all')

        # exited alpine under everything list
        b.wait_present("#containers-containers")
        b.wait_present('#containers-containers tr:contains("test-sh")')
        self.check_container('test-sh', ['test-sh', 'alpine:latest', 'sh', 'exited'])

        # show container listing toggle
        b.click('#containers-containers tbody tr:contains("busybox:latest") td.listing-ct-toggle')
        b.wait_present('#containers-containers tbody tr:contains("busybox:latest") + tr button.btn-delete')

        # show running container
        self.filter_containers('running')
        b.wait_present('#containers-containers tr:contains("busybox:latest")')
        self.check_container('swamped-crate', ['swamped-crate', 'busybox:latest', 'sleep 1000', 'running'])
        # check exited alpine not in running list
        b.wait_not_in_text("#containers-containers", "alpine:latest")

        # delete running container busybox using force delete
        b.click('#containers-containers tbody tr:contains("busybox:latest") + tr button.btn-delete')
        self.confirm_modal("btn-ctr-focedelete")
        b.wait_not_in_text("#containers-containers", "busybox:latest")

        # delete the exited alpine
        self.filter_containers("all")
        b.wait_present('#containers-containers tr:contains("alpine:latest")')
        b.click('#containers-containers tbody tr:contains("alpine:latest") td.listing-ct-toggle')
        b.click('#containers-containers tbody tr:contains("alpine:latest") + tr button.btn-delete')
        self.confirm_modal("btn-ctr-delete")
        b.wait_not_in_text("#containers-containers", "alpine:latest")

        def container_commit(container_name, image_name="testimg", image_tag="testtag", image_author="tester", image_command="sleep 6000"):
            self.filter_containers("all")
            b.wait_present('#containers-containers tr:contains({0})'.format(container_name))
            b.click('#containers-containers tbody tr:contains({0}) td.listing-ct-toggle'.format(container_name))
            # open commit modal and close it using cancel
            b.click('#containers-containers tbody tr:contains({0}) + tr button.btn-commit'.format(container_name))
            self.confirm_modal("btn-ctr-cancel-commit")
            # open commit modal and create an image
            b.click('#containers-containers tbody tr:contains({0}) + tr button.btn-commit'.format(container_name))
            b.wait_present(".modal-dialog div")
            # set commit parameters
            b.set_input_text("#commit-dialog-image-name", image_name)
            b.set_input_text("#commit-dialog-image-tag", image_tag)
            b.set_input_text("#commit-dialog-author", image_author)
            b.set_input_text("#commit-dialog-command", image_command)
            b.click(".modal-dialog div .btn-ctr-commit")
            b.wait_not_present(".modal-dialog div")
            b.wait_present('#containers-images tr:contains("{0}:{1}")'.format(image_name, image_tag))
            b.click('#containers-images tbody tr:contains("{0}:{1}") td.listing-ct-toggle'.format(image_name, image_tag))
            # open the listing toggle of testimg and check the commit paramerters
            b.wait_present('#containers-images tbody tr:contains("{0}:{1}"):has(dd:contains("localhost/{0}:{1}"))'.format(image_name, image_tag))
            b.wait_present('#containers-images tbody tr:contains("{0}:{1}"):has(dd:contains({2}))'.format(image_name, image_tag, image_command))
            b.wait_present('#containers-images tbody tr:contains("{0}:{1}"):has(dd:contains({2}))'.format(image_name, image_tag, image_author))

            # Cleanup
            m.execute("podman rm -f {0}; podman rmi {1}:{2}".format(container_name, image_name, image_tag))

        # run a container (will exit immediately) and test the display of commit modal
        m.execute("podman run -d --name test-sh alpine sh")
        self.filter_containers("all")
        container_commit("test-sh")

        # test commit of a running container
        m.execute("podman run -d --name test-sh busybox sleep 1000")
        self.check_container('test-sh', ['test-sh', 'busybox:latest', 'sleep 1000', 'running'])
        container_commit("test-sh")

        m.execute("podman run -d --name test-sh alpine sh")
        b.wait_present('#containers-containers tr:contains("alpine:latest")')
        b.click('#containers-containers tbody tr:contains("alpine:latest") td.listing-ct-toggle')
        # open commit modal and check error modal
        b.click('#containers-containers tbody tr:contains("alpine:latest") + tr button.btn-commit')
        # check required field error
        b.click(".modal-dialog div .btn-ctr-commit")
        b.wait_present('.modal-dialog div:contains("Image name is required")')
        b.click(".modal-dialog div .alert .close")
        b.wait_not_present(".modal-dialog div .alert")
        # check varlink error
        b.set_input_text("#commit-dialog-image-name", "TEST")
        b.click(".modal-dialog div .btn-ctr-commit")
        b.wait_present(".modal-dialog div")
        b.wait_present('.modal-dialog div:contains("io.podman.ErrorOccurred")')
        b.click(".modal-dialog div .alert .close")
        b.wait_not_present(".modal-dialog div .alert")

        # delete image busybox that hasn't been used
        b.wait_present('#containers-images tr:contains("busybox:latest")')
        b.click('#containers-images tbody tr:contains("busybox:latest") + tr button.btn-delete')
        b.click(".modal-dialog div #btn-img-delete")
        b.wait_not_present("modal-dialog div #btn-img-delete")
        b.wait_not_in_text("#containers-images", "busybox:latest")

        # delete image alpine that has been used by a container
        b.wait_present('#containers-images tr:contains("alpine:latest")')
        b.click('#containers-images tbody tr:contains("alpine:latest") td.listing-ct-toggle')
        b.wait_in_text('#containers-images tbody tr .listing-ct-body:first-child:contains("alpine:latest")', "Command/bin/sh")
        b.click('#containers-images tbody tr:contains("alpine:latest") + tr button.btn-delete')
        b.click(".modal-dialog div #btn-img-delete")
        b.wait_not_present("modal-dialog div #btn-img-delete")
        b.click(".modal-dialog div #btn-img-deleteerror")
        b.wait_not_present("modal-dialog div #btn-img-deleteerror")
        b.wait_not_in_text("#containers-images", "alpine:latest")

    def testDownloadImage(self):
        b = self.browser
        m = self.machine

        def prepare():
            # Create and start registry container
            m.execute("podman run -d -p 5000:5000 --name registry registry:2")
            # Add local insecure registry into resgitries conf
            m.execute("echo \"{0}\" > /etc/containers/registries.conf && systemctl stop io.podman.service".format(REGISTRIES_CONF))
            # Push busybox image to the local registry
            m.execute("podman tag busybox localhost:5000/my-busybox && podman push localhost:5000/my-busybox")
            # Untag busybox image which duplicates the image we are about to download
            m.execute("podman rmi -f busybox")

        class DownloadImageDialog:
            def __init__(self, imageName, imageTag=None):
                self.imageName = imageName
                self.imageTag = imageTag

            def openDialog(self):
                # Open get new image modal
                b.click("caption a:contains(Get new image)")
                b.wait_present('div.modal-dialog div.modal-header h4.modal-title:contains("Search Image")')
                b.wait_present("div.modal-dialog div.modal-footer button:contains(Download):disabled")

                return self

            def fillDialog(self):
                # Search for image specied with self.imageName and self.imageTag
                b.set_input_text("#search-image-dialog-name", self.imageName)
                if self.imageTag:
                    b.set_input_text(".image-tag-entry", self.imageTag)

                return self

            def selectImageAndDownload(self):
                # Select and download the self.imageName image
                b.wait_present("div.list-group div.image-list-item label:contains({0})".format(self.imageName))
                b.click("div.list-group div.image-list-item label:contains({0})".format(self.imageName))
                b.wait_present("div.modal-dialog div.modal-footer button:contains(Download):not([disabled])")
                b.click("div.modal-dialog div.modal-footer button:contains(Download)")

                return self

            def expectDownloadErrorForNonExistingTag(self):
                b.wait_not_present('div.modal-dialog')
                b.wait_present('div.alert:contains("Failed to download image localhost:5000/{0}:{1}")'.format(self.imageName, self.imageTag))

                return self

            def expectSearchErrorForNotExistingImage(self):
                b.wait_present("div.modal-body:contains(No results for {0})".format(self.imageName))

                return self

            def expectDownloadSuccess(self):
                # Confirm that the modal dialog is not open anymore
                b.wait_not_present('div.modal-dialog')
                # Confirm that the image got downloaded
                b.wait_present('#containers-images tr:contains("{0}")'.format(self.imageName))

                return self

            def deleteImage(self):
                if self.imageTag:
                    imageTagSuffix =  ":" + self.imageTag
                else:
                    imageTagSuffix = ""

                # Select the image row
                b.click('#containers-images tbody tr:contains("{0}{1}") td.listing-ct-toggle'.format(self.imageName, imageTagSuffix))

                # Click the delete icon on the image row
                b.wait_present('#containers-images tbody tr:contains("{0}{1}") + tr button.btn-delete'.format(self.imageName, imageTagSuffix))
                b.click('#containers-images tbody tr:contains("{0}{1}") + tr button.btn-delete'.format(self.imageName, imageTagSuffix))

                # Confirm deletion in the delete dialog
                b.click(".modal-dialog div #btn-img-delete")

                b.wait_not_present('#containers-images tr:contains("{0}")'.format(self.imageName))

                return self

        prepare()

        self.login_and_go("/podman")
        b.wait_present("#app")

        dialog = DownloadImageDialog('my-busybox')
        dialog.openDialog() \
              .fillDialog() \
              .selectImageAndDownload() \
              .expectDownloadSuccess() \
              .deleteImage()

        dialog = DownloadImageDialog('my-busybox', 'latest')
        dialog.openDialog() \
              .fillDialog() \
              .selectImageAndDownload() \
              .expectDownloadSuccess() \
              .deleteImage()

        dialog = DownloadImageDialog('foobar')
        dialog.openDialog() \
              .fillDialog() \
              .expectSearchErrorForNotExistingImage()

        dialog = DownloadImageDialog('my-busybox', 'foobar')
        dialog.openDialog() \
              .fillDialog() \
              .selectImageAndDownload() \
              .expectDownloadErrorForNonExistingTag()

    def testLifecycleOperations(self):
        b = self.browser
        m = self.machine

        # run a container
        m.execute("podman run -dit --name swamped-crate busybox sh; podman stop swamped-crate")
        b.wait(lambda: m.execute("podman ps --all | grep -e swamped-crate -e Exited"))

        self.login_and_go("/podman")
        b.wait_present("#app")
        b.wait_present(".content-filter div")
        self.filter_containers('all')

        b.wait_present("#containers-containers")
        b.wait_present('#containers-containers tr:contains("swamped-crate")')
        b.click('#containers-containers tbody tr:contains("busybox:latest") td.listing-ct-toggle')

        # Start the container
        b.click('#containers-containers tbody tr:contains("busybox:latest") + tr button:contains(Start)')

        with b.wait_timeout(5):
            self.check_container('swamped-crate', ['swamped-crate', 'busybox:latest', 'sh', 'running'])

        # Restart the container
        old_pid = m.execute("podman inspect --format '{{.State.Pid}}' swamped-crate".strip())
        b.click('#containers-containers tbody tr:contains("busybox:latest") + tr button:contains(Restart)')
        b.click('#containers-containers tbody tr:contains("busybox:latest") + tr ul.dropdown-menu li a:contains(Force Restart)')
        new_pid = m.execute("podman inspect --format '{{.State.Pid}}' swamped-crate".strip())
        self.assertNotEqual(old_pid, new_pid)

        with b.wait_timeout(5):
            self.check_container('swamped-crate', ['swamped-crate', 'busybox:latest', 'sh', 'running'])

        self.filter_containers('all')
        b.wait_present("#containers-containers")

        # Stop the container
        b.wait_present('#containers-containers tr:contains("busybox:latest")')
        b.click('#containers-containers tbody tr:contains("busybox:latest") + tr button:contains(Stop)')
        b.click('#containers-containers tbody tr:contains("busybox:latest") + tr ul.dropdown-menu li a:contains(Force Stop)')

        self.check_container('swamped-crate', ['swamped-crate', 'busybox:latest', 'sh', 'stopped'])

    def testNotRunning(self):
        b = self.browser
        m = self.machine

        m.execute("systemctl disable --now io.podman.socket")

        self.login_and_go("/podman")

        # Troubleshoot action
        b.click("#app .blank-slate-pf button.btn-default")
        b.enter_page("/system/services")
        b.wait_in_text("#service-unit", "io.podman.socket")

        # Start action, with enabling (by default)
        b.go("/podman")
        b.enter_page("/podman")
        b.click("#app .blank-slate-pf button.btn-primary")

        b.wait_present("#containers-containers")

        self.assertEqual(m.execute("systemctl is-enabled io.podman.socket").strip(), "enabled")
        self.assertEqual(m.execute("systemctl is-active io.podman.socket").strip(), "active")

        # Start action, without enabling
        m.execute("systemctl disable --now io.podman.socket")
        b.click("#app .blank-slate-pf input[type=checkbox]")
        b.click("#app .blank-slate-pf button.btn-primary")

        b.wait_present("#containers-containers")
        self.assertEqual(m.execute("! systemctl is-enabled io.podman.socket").strip(), "disabled")
        self.assertEqual(m.execute("systemctl is-active io.podman.socket").strip(), "active")

        self.allow_journal_messages("/run/podman/io.podman: couldn't connect.*")
        self.allow_restart_journal_messages()

    def testRunImage(self):
        b = self.browser
        m = self.machine

        self.login_and_go("/podman")
        b.wait_in_text("#containers-images", "busybox:latest")
        b.wait_in_text("#containers-images", "alpine:latest")

        # Check command in alpine
        b.wait_present('#containers-images tr:contains("alpine:latest")')
        b.click('#containers-images tbody tr:contains("alpine:latest") td.listing-ct-actions button')
        b.wait_present('div.modal-dialog div.modal-header h4.modal-title:contains("Run Image")')
        b.wait_present("#run-image-dialog-command[value='/bin/sh']")
        b.click(".btn-cancel")

        # Open run image dialog
        b.wait_present('#containers-images tr:contains("busybox:latest")')
        b.click('#containers-images tbody tr:contains("busybox:latest") td.listing-ct-actions button')
        b.wait_present('div.modal-dialog div.modal-header h4.modal-title:contains("Run Image")')

        # Inspect and fill modal dialog
        b.wait_in_text("#run-image-dialog-image", "busybox:latest")

        # Check that there is autogenerated name and then overwrite it
        b.wait_not_val("#run-image-dialog-name", "")
        b.set_input_text("#run-image-dialog-name", "busybox-with-tty")

        b.wait_present("#run-image-dialog-command[value='sh']")

        # Check memory configuration once https://github.com/containers/libpod/pull/2278 in contained in released podman
        b.set_checked("#run-image-dialog-memory-limit-checkbox", True)
        b.wait_present("#run-image-dialog-memory-limit-checkbox:checked")
        b.wait_present('div.modal-body label:contains("Memory Limit") + div.form-inline > input[value="512"]')

        b.set_checked("#run-image-dialog-memory-limit-checkbox", False)
        b.wait_present('div.modal-body label:contains("Memory Limit") + div.form-inline > input[type=number]:disabled')

        # Enable tty
        b.set_checked("#run-image-dialog-tty", True)

        # Set up command line
        b.set_input_text('#run-image-dialog-command', 'sh -c "while echo Hello World; do sleep 1; done"')

        # Configure published ports
        b.set_input_text('.publish-port-form[data-key="0"] input:first-child', '5000')
        b.set_input_text('.publish-port-form[data-key="0"] input:nth-child(2)', '6000')
        b.click('.publish-port-form[data-key="0"] .fa-plus')
        b.set_input_text('.publish-port-form[data-key="1"] input:first-child', '5001')
        b.set_input_text('.publish-port-form[data-key="1"] input:nth-child(2)', '6001')
        b.set_val('.publish-port-form[data-key="1"] select', "UDP")
        b.click('.publish-port-form[data-key="1"] .fa-plus')
        b.set_input_text('.publish-port-form[data-key="2"] input:first-child', '7001')
        b.set_input_text('.publish-port-form[data-key="2"] input:nth-child(2)', '7001')
        b.click('.publish-port-form[data-key="2"] .pficon-close')
        b.click('.publish-port-form[data-key="1"] .fa-plus')
        b.set_input_text('.publish-port-form[data-key="3"] input:first-child', '8001')
        b.set_input_text('.publish-port-form[data-key="3"] input:nth-child(2)', '8001')

        # Configure env
        b.set_input_text('.env-form[data-key="0"] input:first-child', 'APPLE')
        b.set_input_text('.env-form[data-key="0"] input:last-child', 'ORANGE')
        b.click('.env-form[data-key="0"] .fa-plus')
        b.set_input_text('.env-form[data-key="1"] input:first-child', 'PEAR')
        b.set_input_text('.env-form[data-key="1"] input:last-child', 'BANANA')
        b.click('.env-form[data-key="1"] .fa-plus')
        b.set_input_text('.env-form[data-key="2"] input:first-child', 'MELON')
        b.set_input_text('.env-form[data-key="2"] input:last-child', 'GRAPE')
        b.click('.env-form[data-key="2"] .pficon-close')
        b.click('.env-form[data-key="1"] .fa-plus')
        b.set_input_text('.env-form[data-key="3"] input:first-child', 'RHUBARB')
        b.set_input_text('.env-form[data-key="3"] input:last-child', 'STRAWBERRY')

        # Configure volumes
        rodir, rwdir = m.execute("mktemp; mktemp").split('\n')[:2]
        b.set_val('.volume-form[data-key="0"] select', "ro")
        b.set_input_text('.volume-form[data-key="0"] input:first-child', rodir)
        b.set_input_text('.volume-form[data-key="0"] input:nth-child(2)', '/tmp/ro')
        b.wait_val('.volume-form[data-key="0"] input:first-child', rodir)
        b.wait_not_present('.volume-form[data-key="0"] .pending-callback')
        b.click('.volume-form[data-key="0"] .fa-plus')
        b.click('.volume-form[data-key="1"] .pficon-close')
        b.wait_not_present('.volume-form[data-key="1"]')
        b.click('.volume-form[data-key="0"] .fa-plus')
        b.set_input_text('.volume-form[data-key="2"] input:first-child', rwdir)
        b.set_input_text('.volume-form[data-key="2"] input:nth-child(2)', '/tmp/rw')
        b.wait_val('.volume-form[data-key="2"] input:first-child', rwdir)
        b.wait_not_present('.volume-form[data-key="2"] .pending-callback')

        b.click('div.modal-footer button:contains("Run")')
        b.wait_not_present("div.modal-dialog")
        b.wait_present('#containers-containers tr:contains("busybox:latest")')
        self.check_container('busybox-with-tty', ['busybox-with-tty', 'busybox:latest', 'sh -c "while echo Hello World; do sleep 1; done"', 'running'])

        hasTTY = m.execute("podman inspect --format '{{.Config.Tty}}' busybox-with-tty").strip()
        self.assertEqual(hasTTY, 'true')
        b.wait(lambda: "Hello World" in m.execute("podman logs busybox-with-tty"))

        b.click('#containers-containers tbody tr:contains("busybox:latest") td.listing-ct-toggle')
        b.wait_in_text('#containers-containers tr:contains("busybox:latest") dt:contains("Ports") + dd', '0.0.0.0:6000 \u2192 5000/tcp')
        b.wait_in_text('#containers-containers tr:contains("busybox:latest") dt:contains("Ports") + dd', '0.0.0.0:6001 \u2192 5001/udp')
        b.wait_in_text('#containers-containers tr:contains("busybox:latest") dt:contains("Ports") + dd', '0.0.0.0:8001 \u2192 8001/tcp')
        b.wait_not_in_text('#containers-containers tr:contains("busybox:latest") dt:contains("Ports") + dd', '0.0.0.0:7001 \u2192 7001/tcp')
        ports = m.execute("podman inspect --format '{{.NetworkSettings.Ports}}' busybox-with-tty")
        self.assertIn('6000 5000 tcp', ports)
        self.assertIn('6001 5001 udp', ports)
        self.assertIn('8001 8001 tcp', ports)
        self.assertNotIn('7001 7001 tcp', ports)

        env = m.execute("podman exec busybox-with-tty env")
        self.assertIn('APPLE=ORANGE', env)
        self.assertIn('PEAR=BANANA', env)
        self.assertIn('RHUBARB=STRAWBERRY', env)
        self.assertNotIn('MELON=GRAPE', env)

        romnt = m.execute("podman exec busybox-with-tty cat /proc/self/mountinfo | grep /tmp/ro")
        self.assertIn('ro', romnt)
        self.assertIn(rodir[4:], romnt)
        rwmnt = m.execute("podman exec busybox-with-tty cat /proc/self/mountinfo | grep /tmp/rw")
        self.assertIn('rw', rwmnt)
        self.assertIn(rwdir[4:], rwmnt)

        # Create another instance without port publishing
        b.wait_present('#containers-images tr:contains("busybox:latest")')
        b.click('#containers-images tbody tr:contains("busybox:latest") td.listing-ct-actions button')
        b.wait_present('div.modal-dialog div.modal-header h4.modal-title:contains("Run Image")')

        b.wait_in_text("#run-image-dialog-image", "busybox:latest")
        b.set_input_text("#run-image-dialog-name", "busybox-without-publish")

        b.click('div.modal-footer button:contains("Run")')
        b.wait_not_present("div.modal-dialog")
        b.wait_present('#containers-containers tr:contains("busybox-without-publish")')
        b.click('#containers-containers tbody tr:contains("busybox-without-publish") td.listing-ct-toggle')
        b.wait_present('#containers-containers tbody tr:contains("busybox-without-publish") + tr dt:contains("Ports")')
        b.wait_text('#containers-containers tr:contains("busybox-without-publish") + tr dt:contains("Ports") + dd', "")

        b.set_input_text('#containers-filter', 'tty')
        self.check_containers(["busybox-with-tty"], ["busybox-without-publish"])
        self.check_images([], ["busybox:latest", "alpine:latest", "registry:2"])
        b.set_input_text('#containers-filter', 'busy')
        self.check_containers(["busybox-with-tty", "busybox-without-publish"], [])
        self.check_images(["busybox:latest"], ["alpine:latest", "registry:2"])
        b.set_input_text('#containers-filter', 'alpine')
        self.check_containers([], ["busybox-with-tty", "busybox-without-publish"])
        self.check_images(["alpine:latest"], ["busybox:latest", "registry:2"])
        b.set_input_text('#containers-filter', '')
        self.check_containers(["busybox-with-tty", "busybox-without-publish"], [])
        self.check_images(["busybox:latest", "alpine:latest", "registry:2"], [])

        b.set_input_text('#containers-filter', 'foobar')
        b.wait_present('#containers-containers thead tr td:contains("No containers that match the current filter")')
        b.wait_present('#containers-images thead tr td:contains("No images that match the current filter")')
        b.set_input_text('#containers-filter', '')
        m.execute("podman rmi -f $(podman images -q)")
        b.wait_present('#containers-containers thead tr td:contains("No running containers")')
        b.wait_present('#containers-images thead tr td:contains("No images")')
        b.set_val("#containers-containers-filter", "all")
        b.wait_present('#containers-containers thead tr td:contains("No containers")')

    def check_content(self, type, present, not_present):
        b = self.browser
        for item in present:
            b.wait_visible('#containers-{0} tbody tr:contains({1})'.format(type, item))
        for item in not_present:
            b.wait_not_present('#containers-{0} tbody tr:contains({1})'.format(type, item))

    def check_containers(self, present, not_present):
        self.check_content("containers", present, not_present)

    def check_images(self, present, not_present):
        self.check_content("images", present, not_present)

    def check_container(self, row_name, expected_strings):
        """Check the container with row_name has the expected_string shown in the row"""
        b = self.browser
        for str in expected_strings:
            b.wait_in_text('#containers-containers tr:contains(%s)' % row_name, str)

    def filter_containers(self, value):
        """Use dropdown menu in the header to filter containers"""
        b = self.browser
        b.set_val("#containers-containers-filter", value)

    def confirm_modal(self, key_type):
        """Wait for the pop up window and click the button with className as key_type"""
        b = self.browser
        b.wait_present(".modal-dialog div")
        b.wait_present(".modal-dialog div .%s" % key_type)
        b.click(".modal-dialog div .%s" % key_type)
        b.wait_not_present(".modal-dialog div .%s" % key_type)

if __name__ == '__main__':
    testlib.test_main()
