Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
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,
),
)
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")
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
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")
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]'):
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
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)))
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', {})
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)
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",