How to use the xiblint.rules.Rule function in xiblint

To help you get started, we’ve selected a few xiblint examples, based on popular ways it is used in public projects.

Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.

github lyft / xiblint / xiblint / rules / color_assets.py View on Github external
from xiblint.rules import Rule
from xiblint.xibcontext import XibContext
import glob
import json


class ColorAssets(Rule):
    def __init__(self, config):
        asset_catalog_path = config.get("asset_catalog", None)
        if asset_catalog_path is None:
            raise SystemExit(
                "error: Asset catalog not found. Please configure 'asset_catalog'.",
            )

        self.assets = glob.glob("{}/**/*.colorset".format(asset_catalog_path))

        if not self.assets:
            raise SystemExit(
                "error: Failed to load asset catalog at: '{}'".format(
                    asset_catalog_path,
                ),
            )
github lyft / xiblint / xiblint / rules / accessibility_labels_for_images.py View on Github external
from xiblint.rules import Rule
from xiblint.xibcontext import XibContext
from xiblint.xibutils import (
    view_is_accessibility_element,
    view_accessibility_label,
    get_view_user_defined_attr,
)


class AccessibilityLabelsForImages(Rule):
    """
    Checks for accessible images with no accessibility label.
    In this case, VoiceOver will announce the image asset's name, which might be unwanted.
    """
    def check(self, context):  # type: (XibContext) -> None
        for image_view in context.tree.findall(".//imageView"):
            if (
                    view_is_accessibility_element(image_view) is True and
                    not view_accessibility_label(image_view) and
                    not get_view_user_defined_attr(image_view, 'accessibilityFormat')
            ):
                context.error(image_view, "Image is accessible but has no accessibility label")
github lyft / xiblint / xiblint / rules / strict_font_sizes.py View on Github external
from xiblint.rules import Rule
from xiblint.xibcontext import XibContext


class StrictFontSizes(Rule):
    """
    Ensures fonts are in the allowed set.

    Example configuration:
    {
      "minimum_size": 13,
      "maximum_size": 30
    }
    """
    def check(self, context):  # type: (XibContext) -> None
        minimum_size = self.config.get('minimum_size', 0)
        maximum_size = self.config.get('maximum_size', 1000)

        for element in context.tree.findall('.//font') + context.tree.findall('.//fontDescription'):
            attribute_name = None
github lyft / xiblint / xiblint / rules / no_trait_variations.py View on Github external
from xiblint.rules import Rule
from xiblint.xibcontext import XibContext


class NoTraitVariations(Rule):
    """
    Checks for Trait Variations being enabled.
    """
    def check(self, context):  # type: (XibContext) -> None
        root = context.tree.getroot()
        if root.get('useTraitCollections') == 'YES' and root.get('targetRuntime') != 'watchKit':
            context.error(root, "Document has 'Use Trait Variations' enabled")
github lyft / xiblint / xiblint / rules / unavailable_custom_classes.py View on Github external
from xml.etree.ElementTree import Element

from xiblint.rules import Rule
from xiblint.xibcontext import XibContext


class UnavailableCustomClasses(Rule):
    """
    Ensures a given custom class isn't used and provides a replacement suggestion.

    You must specify a module as part of the class name.

    Example configuration:
    {
      "custom_classes": {
          "SomeModule.LegacyButton": "SomeModule.NewButton"
      }
    }
    """
    def check(self, context):  # type: (XibContext) -> None
        unavailable_classes = self.config.get('custom_classes', {})

        for element in context.tree.findall('.//*[@customClass]'):
github lyft / xiblint / xiblint / rules / accessibility_labels_for_image_buttons.py View on Github external
from xiblint.rules import Rule
from xiblint.xibcontext import XibContext
from xiblint.xibutils import (
    view_is_accessibility_element,
    view_accessibility_label,
    get_view_user_defined_attr,
)


class AccessibilityLabelsForImageButtons(Rule):
    """
    Checks for image buttons with no accessibility label.
    In this case, VoiceOver will announce the image asset's name, which might be unwanted.
    """
    def check(self, context):  # type: (XibContext) -> None
        for button in context.tree.findall(".//button"):
            state_normal = button.find("./state[@key='normal']")
            if (
                    state_normal is None or
                    'title' in state_normal.attrib or
                    view_is_accessibility_element(button) is False or
                    view_accessibility_label(button) or
                    get_view_user_defined_attr(button, 'accessibilityFormat')
            ):
                continue
github lyft / xiblint / xiblint / rules / simulated_metrics_retina4_0.py View on Github external
from xiblint.rules import Rule
from xiblint.xibcontext import XibContext

ALLOWED_DEVICES = [
    'retina4_0',
    'watch38',
]


class SimulatedMetricsRetina40(Rule):
    """
    Ensures simulated metrics are for the iPhone SE or a 38mm watch
    which are currently the smallest display profiles.
    """
    def check(self, context):  # type: (XibContext) -> None
        root = context.tree.getroot()

        # In Xcode 9 this metadata is in a new place
        device = root.find('device')
        if device is not None and device.get('id') not in ALLOWED_DEVICES:
            context.error(
                device,
                'Simulated metrics ("View As:") must be one of: {}'
                .format(', '.join(ALLOWED_DEVICES)))
github lyft / xiblint / xiblint / rules / unavailable_system_classes.py View on Github external
from xml.etree.ElementTree import Element

from xiblint.rules import Rule
from xiblint.xibcontext import XibContext


class UnavailableSystemClasses(Rule):
    """
    Ensures given system types are subclassed by a set of classes.

    You must specify a module as part of the class name.

    Example configuration:
    {
      "system_classes": {
          "navigationController": ["ModuleName.CustomNavigationController"],
          "button": ["ModuleName.CoolButton", "ModuleName.CoolerButton"]
      }
    }
    """
    def check(self, context):  # type: (XibContext) -> None
        custom_classes = self.config.get('system_classes', {})
github lyft / xiblint / xiblint / rules / accessibility_format.py View on Github external
import re

from xiblint.rules import Rule
from xiblint.xibcontext import XibContext
from xiblint.xibutils import (
    get_object_id,
    get_view_user_defined_attr,
    view_accessibility_identifier,
)


class AccessibilityFormat(Rule):
    """
    Checks for incorrect use of Lyft extensions `accessibilityFormat` and `accessibilitySources`.
    """
    def check(self, context):  # type: (XibContext) -> None
        views_with_accessibility_format = {
            element.parent.parent
            for element in context.tree.findall(
                ".//userDefinedRuntimeAttributes/userDefinedRuntimeAttribute[@keyPath='accessibilityFormat']")
        }
        views_with_accessibility_sources = {
            element.parent.parent
            for element in context.tree.findall(".//connections/outletCollection[@property='accessibilitySources']")
        }

        for view in views_with_accessibility_format | views_with_accessibility_sources:
            check_view(context, view)
github lyft / xiblint / xiblint / rules / automation_identifiers_for_outlet_labels.py View on Github external
from xiblint.rules import Rule
from xiblint.xibcontext import XibContext


class AutomationIdentifiersForOutletLabels(Rule):
    """
    Checks for labels with outlets into a view controller that have no accessibility identifiers.
    Labels with outlets might get dynamic text, and therefore should be accessible to UI testing.
    """
    def check(self, context):  # type: (XibContext) -> None
        for outlet in context.tree.findall(".//viewController/connections/outlet"):
            destination = outlet.get('destination')
            label = context.tree.find(".//label[@id='{}']".format(destination))
            if label is None:
                continue
            if label.find('./accessibility[@identifier]') is not None:
                continue
            context.error(
                label,
                "Label with text '{}' has an outlet into the view controller "
                "so it requires an accessibility identifier",