Пример #1
0
class TestIXBRLViewer(unittest.TestCase):
    def setUp(self):
        self.usd_qname = Mock(localName='USD',
                              prefix='iso4217',
                              namespaceURI='http://www.xbrl.org/2003/iso4217')

        self.usd_unit = Mock(measures=([self.usd_qname], []))

        self.null_units = Mock(measures=([], []))

        self.cash_concept = Mock(qname=Mock(localName='Cash',
                                            prefix='us-gaap',
                                            namespaceURI='http://viewer.com'))

        to_concept = Mock(qname=Mock(localName='to_concept',
                                     prefix='us-gaap',
                                     namespaceURI='http://viewer.com'))
        from_concept = Mock(qname=Mock(localName='from_concept',
                                       prefix='us-gaap',
                                       namespaceURI='http://viewer.com'))

        dimension_concept = Mock(qname=Mock(localName='dimension',
                                            prefix='us-gaap',
                                            namespaceURI='http://viewer.com'))

        member_concept = Mock(qname=Mock(localName='member',
                                         prefix='us-gaap',
                                         namespaceURI='http://viewer.com'))

        rel = Mock(fromModelObject=from_concept,
                   toModelObject=to_concept,
                   weight=1)

        dimension = Mock(dimensionQname=dimension_concept.qname,
                         memberQname=member_concept.qname,
                         dimension=dimension_concept,
                         member=member_concept)

        typed_dimension = Mock(dimensionQname=dimension_concept.qname,
                               memberQname=None,
                               dimension=dimension_concept,
                               typedMember=Mock(text='typedDimension'))

        dimension_missing_member = Mock(dimensionQname=dimension_concept.qname,
                                        memberQname=None,
                                        dimension=dimension_concept,
                                        typedMember=None)

        def isoformat_effect():
            return '01-01-19T00:00:00'

        context_1 = Mock(entityIdentifier=('scheme', 'ident'),
                         qnameDims={'d': dimension},
                         isInstantPeriod=False,
                         isStartEndPeriod=True,
                         startDatetime=Mock(isoformat=isoformat_effect),
                         endDatetime=Mock(isoformat=isoformat_effect))

        context_2 = Mock(entityIdentifier=('scheme', 'ident'),
                         qnameDims={},
                         isInstantPeriod=None,
                         isStartEndPeriod=None)

        context_with_typed_dimension = Mock(
            entityIdentifier=('scheme', 'ident'),
            qnameDims={'d': typed_dimension},
            isInstantPeriod=False,
            isStartEndPeriod=True,
            startDatetime=Mock(isoformat=isoformat_effect),
            endDatetime=Mock(isoformat=isoformat_effect))

        context_with_missing_member_on_dimension = Mock(
            entityIdentifier=('scheme', 'ident'),
            qnameDims={'d': dimension_missing_member},
            isInstantPeriod=False,
            isStartEndPeriod=True,
            startDatetime=Mock(isoformat=isoformat_effect),
            endDatetime=Mock(isoformat=isoformat_effect))

        fact_1 = Mock(id='fact_id1',
                      qname=self.cash_concept.qname,
                      value=100,
                      isNumeric=False,
                      context=context_1,
                      concept=self.cash_concept,
                      format='format')

        fact_2 = Mock(id='fact_id2',
                      qname=self.cash_concept.qname,
                      concept=self.cash_concept,
                      context=context_2,
                      isNumeric=True,
                      unit=self.usd_unit,
                      value=None,
                      decimals=None,
                      precision=None,
                      format=None)

        fact_3 = Mock(id='fact_id3',
                      qname=self.cash_concept.qname,
                      concept=self.cash_concept,
                      context=context_2,
                      isNumeric=True,
                      unit=self.null_units,
                      value=None,
                      decimals=None,
                      precision=None,
                      format=None)

        fact_with_typed_dimension = Mock(id='fact_typed_dimension',
                                         qname=self.cash_concept.qname,
                                         value=10,
                                         isNumeric=False,
                                         context=context_with_typed_dimension,
                                         concept=self.cash_concept,
                                         format='format')

        fact_with_missing_member_on_dimension = Mock(
            id='fact_dimension_missing_member',
            qname=self.cash_concept.qname,
            value=1000,
            isNumeric=False,
            context=context_with_missing_member_on_dimension,
            concept=self.cash_concept,
            format='format')

        def fromModelObjects_effect(concept):
            return []

        def relationshipSet_effect(self, *args):
            return Mock(fromModelObject=fromModelObjects_effect,
                        modelRelationships=[rel])

        def info_effect(info, msg):
            # This is a no op for logging
            pass

        baseSets = defaultdict(list)
        baseSets[('http://www.xbrl.org/2003/arcrole/summation-item', 'ELR',
                  'linkqname', 'arcqname')] = []
        baseSets[("http://xbrl.org/int/dim/arcrole/dimension-default", 'ELR',
                  'linkqname', 'arcqname')] = []
        baseSets[("http://www.xbrl.org/2003/arcrole/parent-child", 'ELR',
                  'linkqname', 'arcqname')] = []

        roleTypes = defaultdict(list)
        roleTypes['ELR'] = [Mock(definition="ELR Label")]

        root = lxml.etree.Element('root')
        lxml.etree.SubElement(root, '{http://www.w3.org/1999/xhtml}body')

        self.modelDocument = Mock(xmlDocument=lxml.etree.ElementTree(root),
                                  filepath='')

        error1 = logging.LogRecord("arelle", logging.ERROR, "", 0,
                                   "Error message", {}, None)
        error1.messageCode = "code1"
        self.modelManager = Mock(cntlr=Mock(logHandler=Mock(
            logRecordBuffer=[error1])))

        self.modelXbrl_1 = Mock(relationshipSet=relationshipSet_effect,
                                relationshipSets={},
                                baseSets=baseSets,
                                roleTypes=roleTypes,
                                facts=[
                                    fact_1, fact_with_typed_dimension,
                                    fact_with_missing_member_on_dimension
                                ],
                                info=info_effect,
                                modelDocument=self.modelDocument,
                                modelManager=self.modelManager)
        self.modelXbrl_2 = Mock(relationshipSet=relationshipSet_effect,
                                relationshipSets={},
                                baseSets=baseSets,
                                roleTypes=roleTypes,
                                facts=[fact_2, fact_3],
                                info=info_effect,
                                modelDocument=self.modelDocument,
                                modelManager=self.modelManager)

        self.cash_concept.modelXbrl = self.modelXbrl_1
        to_concept.modelXbrl = self.modelXbrl_1
        from_concept.modelXbrl = self.modelXbrl_1
        dimension_concept.modelXbrl = self.modelXbrl_1
        member_concept.modelXbrl = self.modelXbrl_1
        self.builder_1 = IXBRLViewerBuilder(self.modelXbrl_1)
        self.builder_2 = IXBRLViewerBuilder(self.modelXbrl_1)
        self.builder_3 = IXBRLViewerBuilder(self.modelXbrl_2)

    @patch('arelle.XbrlConst.conceptLabel',
           'http://www.xbrl.org/2003/arcrole/concept-label')
    @patch('arelle.XbrlConst.conceptReference',
           'http://www.xbrl.org/2003/arcrole/concept-reference')
    def test_addConcept_simple_case(self):
        self.builder_1.addConcept(self.cash_concept)
        self.assertTrue(
            self.builder_1.taxonomyData.get('concepts').get('us-gaap:Cash'))

    @patch('arelle.XbrlConst.parentChild',
           'http://www.xbrl.org/2003/arcrole/parent-child')
    @patch('arelle.XbrlConst.summationItem',
           'http://www.xbrl.org/2003/arcrole/summation-item')
    def test_getRelationships_simple_case(self):
        modelXbrl = Mock(baseSets=defaultdict(list), relationshipSets={})
        builder = IXBRLViewerBuilder(modelXbrl)
        result = builder.getRelationships()
        self.assertDictEqual(result, {})

    @patch('arelle.XbrlConst.parentChild',
           'http://www.xbrl.org/2003/arcrole/parent-child')
    @patch('arelle.XbrlConst.summationItem',
           'http://www.xbrl.org/2003/arcrole/summation-item')
    def test_getRelationships_returns_a_rel(self):
        result = self.builder_1.getRelationships()
        roleMap = self.builder_1.roleMap
        siPrefix = roleMap.getPrefix(
            'http://www.xbrl.org/2003/arcrole/summation-item')
        self.assertTrue(
            result.get(siPrefix).get(
                roleMap.getPrefix('ELR')).get('us-gaap:from_concept'))

    def test_addELR_no_definition(self):
        """
        Adding an ELR with no definition should result in an "en" label with
        the roleURI as its value
        """
        elr = "http://example.com/unknownELR"
        self.builder_1.addELR(elr)
        elrPrefix = self.builder_1.roleMap.getPrefix(elr)
        self.assertEqual(
            self.builder_1.taxonomyData.get('roleDefs').get(elrPrefix).get(
                "en"), elr)

    def test_addELR_with_definition(self):
        """
        Adding an ELR with no definition should result in an "en" label with
        the roleURI as its value
        """
        elr = "ELR"
        self.builder_1.addELR(elr)
        elrPrefix = self.builder_1.roleMap.getPrefix(elr)
        self.assertEqual(
            self.builder_1.taxonomyData.get('roleDefs').get(elrPrefix).get(
                "en"), "ELR Label")

    @patch('arelle.XbrlConst.conceptLabel',
           'http://www.xbrl.org/2003/arcrole/concept-label')
    @patch('arelle.XbrlConst.conceptReference',
           'http://www.xbrl.org/2003/arcrole/concept-reference')
    @patch('arelle.XbrlConst.dimensionDefault',
           'http://xbrl.org/int/dim/arcrole/dimension-default')
    @patch('arelle.XbrlConst.parentChild',
           'http://www.xbrl.org/2003/arcrole/parent-child')
    @patch('arelle.XbrlConst.summationItem',
           'http://www.xbrl.org/2003/arcrole/summation-item')
    @patch('arelle.XbrlConst.standardLabel',
           'http://www.xbrl.org/2003/role/label')
    @patch('arelle.XbrlConst.documentationLabel',
           'http://www.xbrl.org/2003/role/documentation')
    def test_createViewerWithValidation(self):
        js_uri = 'ixbrlviewer.js'
        result = self.builder_1.createViewer(js_uri)
        self.assertEqual(len(result.files), 1)
        body = result.files[0].xmlDocument.getroot()[0]
        self.assertEqual(body[0].text, 'BEGIN IXBRL VIEWER EXTENSIONS')
        self.assertEqual(body[1].attrib.get('src'), js_uri)
        self.assertEqual(body[1].attrib.get('type'), 'text/javascript')
        self.assertEqual(body[2].attrib.get('type'),
                         'application/x.ixbrl-viewer+json')
        self.assertEqual(body[3].text, 'END IXBRL VIEWER EXTENSIONS')

        jsdata = json.loads(body[2].text)
        errors = jsdata["validation"]
        self.assertEqual(errors, [{
            "sev": "ERROR",
            "msg": "Error message",
            "code": "code1"
        }])
        self.assertEqual(set(jsdata["facts"]), {
            "fact_id1", "fact_typed_dimension", "fact_dimension_missing_member"
        })

    @patch('arelle.XbrlConst.conceptLabel',
           'http://www.xbrl.org/2003/arcrole/concept-label')
    @patch('arelle.XbrlConst.conceptReference',
           'http://www.xbrl.org/2003/arcrole/concept-reference')
    @patch('arelle.XbrlConst.parentChild',
           'http://www.xbrl.org/2003/arcrole/parent-child')
    @patch('arelle.XbrlConst.summationItem',
           'http://www.xbrl.org/2003/arcrole/summation-item')
    @patch('arelle.XbrlConst.standardLabel',
           'http://www.xbrl.org/2003/role/label')
    @patch('arelle.XbrlConst.documentationLabel',
           'http://www.xbrl.org/2003/role/documentation')
    @patch('arelle.XbrlConst.dimensionDefault',
           'http://xbrl.org/int/dim/arcrole/dimension-default')
    def test_createViewer(self):
        js_uri = 'ixbrlviewer.js'
        result = self.builder_2.createViewer(js_uri, showValidations=False)
        self.assertEqual(len(result.files), 1)
        body = result.files[0].xmlDocument.getroot()[0]
        self.assertEqual(body[0].text, 'BEGIN IXBRL VIEWER EXTENSIONS')
        self.assertEqual(body[1].attrib.get('src'), js_uri)
        self.assertEqual(body[1].attrib.get('type'), 'text/javascript')
        self.assertEqual(body[2].attrib.get('type'),
                         'application/x.ixbrl-viewer+json')
        self.assertEqual(body[3].text, 'END IXBRL VIEWER EXTENSIONS')

        jsdata = json.loads(body[2].text)
        self.assertNotIn("validation", jsdata)
        self.assertEqual(set(jsdata["facts"]), {
            "fact_id1", "fact_typed_dimension", "fact_dimension_missing_member"
        })

    @patch('arelle.XbrlConst.conceptLabel',
           'http://www.xbrl.org/2003/arcrole/concept-label')
    @patch('arelle.XbrlConst.conceptReference',
           'http://www.xbrl.org/2003/arcrole/concept-reference')
    @patch('arelle.XbrlConst.parentChild',
           'http://www.xbrl.org/2003/arcrole/parent-child')
    @patch('arelle.XbrlConst.dimensionDefault',
           'http://xbrl.org/int/dim/arcrole/dimension-default')
    @patch('arelle.XbrlConst.summationItem',
           'http://www.xbrl.org/2003/arcrole/summation-item')
    @patch('arelle.XbrlConst.standardLabel',
           'http://www.xbrl.org/2003/role/label')
    @patch('arelle.XbrlConst.documentationLabel',
           'http://www.xbrl.org/2003/role/documentation')
    def test_createViewer_bad_path(self):
        js_uri = 'ixbrlviewer.js'
        result = self.builder_3.createViewer(js_uri)
        self.assertEqual(len(result.files), 1)
        body = result.files[0].xmlDocument.getroot()[0]
        self.assertEqual(body[0].text, 'BEGIN IXBRL VIEWER EXTENSIONS')
        self.assertEqual(body[1].attrib.get('src'), js_uri)
        self.assertEqual(body[1].attrib.get('type'), 'text/javascript')
        self.assertEqual(body[2].attrib.get('type'),
                         'application/x.ixbrl-viewer+json')
        self.assertEqual(body[3].text, 'END IXBRL VIEWER EXTENSIONS')

        jsdata = json.loads(body[2].text)
        facts = jsdata["facts"]
        self.assertEqual(facts.keys(), {"fact_id2", "fact_id3"})
        self.assertEqual(facts["fact_id2"]["a"]["u"], "iso4217:USD")
        self.assertEqual(facts["fact_id3"]["a"]["u"], None)
Пример #2
0
class TestIXBRLViewer(unittest.TestCase):
    def setUp(self):
        self.cash_concept = Mock(qname=Mock(localName='Cash',
                                            prefix='us-gaap',
                                            namespaceURI='http://viewer.com'))

        to_concept = Mock(qname=Mock(localName='to_concept',
                                     prefix='us-gaap',
                                     namespaceURI='http://viewer.com'))
        from_concept = Mock(qname=Mock(localName='from_concept',
                                       prefix='us-gaap',
                                       namespaceURI='http://viewer.com'))

        dimension_concept = Mock(qname=Mock(localName='dimension',
                                            prefix='us-gaap',
                                            namespaceURI='http://viewer.com'))

        member_concept = Mock(qname=Mock(localName='member',
                                         prefix='us-gaap',
                                         namespaceURI='http://viewer.com'))

        rel = Mock(fromModelObject=from_concept,
                   toModelObject=to_concept,
                   weight=1)

        dimension = Mock(dimensionQname=dimension_concept.qname,
                         memberQname=member_concept.qname,
                         dimension=dimension_concept,
                         member=member_concept)

        def isoformat_effect():
            return '01-01-19T00:00:00'

        context_1 = Mock(entityIdentifier=('scheme', 'ident'),
                         qnameDims={'d': dimension},
                         isInstantPeriod=False,
                         isStartEndPeriod=True,
                         startDatetime=Mock(isoformat=isoformat_effect),
                         endDatetime=Mock(isoformat=isoformat_effect))

        context_2 = Mock(entityIdentifier=('scheme', 'ident'),
                         qnameDims={},
                         isInstantPeriod=None,
                         isStartEndPeriod=None)

        fact_1 = Mock(id='fact_id',
                      qname=self.cash_concept.qname,
                      value=100,
                      isNumeric=False,
                      context=context_1,
                      concept=self.cash_concept,
                      format='format')

        fact_2 = Mock(id='fact_id',
                      qname=self.cash_concept.qname,
                      concept=self.cash_concept,
                      context=context_2,
                      isNumeric=True,
                      unit=None,
                      value=None,
                      decimals=None,
                      precision=None,
                      format=None)

        def fromModelObjects_effect(concept):
            return []

        def relationshipSet_effect(self, *args):
            return Mock(fromModelObject=fromModelObjects_effect,
                        modelRelationships=[rel])

        def info_effect(info, msg):
            # This is a no op for logging
            pass

        baseSets = defaultdict(list)
        baseSets[('http://www.xbrl.org/2003/arcrole/summation-item', 'ELR',
                  'linkqname', 'arcqname')] = []

        roleTypes = defaultdict(list)
        roleTypes['ELR'] = [Mock(definition="ELR Label")]

        root = lxml.etree.Element('root')
        lxml.etree.SubElement(root, '{http://www.w3.org/1999/xhtml}body')

        self.modelDocument = Mock(xmlDocument=lxml.etree.ElementTree(root),
                                  filepath='')

        self.modelXbrl_1 = Mock(relationshipSet=relationshipSet_effect,
                                baseSets=baseSets,
                                roleTypes=roleTypes,
                                facts=[fact_1],
                                info=info_effect,
                                modelDocument=self.modelDocument)
        self.modelXbrl_2 = Mock(relationshipSet=relationshipSet_effect,
                                baseSets=baseSets,
                                roleTypes=roleTypes,
                                facts=[fact_2],
                                info=info_effect,
                                modelDocument=self.modelDocument)

        self.cash_concept.modelXbrl = self.modelXbrl_1
        to_concept.modelXbrl = self.modelXbrl_1
        from_concept.modelXbrl = self.modelXbrl_1
        dimension_concept.modelXbrl = self.modelXbrl_1
        member_concept.modelXbrl = self.modelXbrl_1
        self.builder_1 = IXBRLViewerBuilder(self.modelXbrl_1)
        self.builder_2 = IXBRLViewerBuilder(self.modelXbrl_2)

    @patch('arelle.XbrlConst.conceptLabel',
           'http://www.xbrl.org/2003/arcrole/concept-label')
    @patch('arelle.XbrlConst.conceptReference',
           'http://www.xbrl.org/2003/arcrole/concept-reference')
    def test_addConcept_simple_case(self):
        self.builder_1.addConcept(self.cash_concept)
        self.assertTrue(
            self.builder_1.taxonomyData.get('concepts').get('us-gaap:Cash'))

    @patch('arelle.XbrlConst.parentChild',
           'http://www.xbrl.org/2003/arcrole/parent-child')
    @patch('arelle.XbrlConst.summationItem',
           'http://www.xbrl.org/2003/arcrole/summation-item')
    def test_getRelationships_simple_case(self):
        modelXbrl = Mock(baseSets=defaultdict(list))
        builder = IXBRLViewerBuilder(modelXbrl)
        result = builder.getRelationships()
        self.assertDictEqual(result, {})

    @patch('arelle.XbrlConst.parentChild',
           'http://www.xbrl.org/2003/arcrole/parent-child')
    @patch('arelle.XbrlConst.summationItem',
           'http://www.xbrl.org/2003/arcrole/summation-item')
    def test_getRelationships_returns_a_rel(self):
        result = self.builder_1.getRelationships()
        roleMap = self.builder_1.roleMap
        siPrefix = roleMap.getPrefix(
            'http://www.xbrl.org/2003/arcrole/summation-item')
        self.assertTrue(
            result.get(siPrefix).get(
                roleMap.getPrefix('ELR')).get('us-gaap:from_concept'))

    def test_addELR_no_definition(self):
        """
        Adding an ELR with no definition should result in an "en" label with
        the roleURI as its value
        """
        elr = "http://example.com/unknownELR"
        self.builder_1.addELR(elr)
        elrPrefix = self.builder_1.roleMap.getPrefix(elr)
        self.assertEqual(
            self.builder_1.taxonomyData.get('roleDefs').get(elrPrefix).get(
                "en"), elr)

    def test_addELR_with_definition(self):
        """
        Adding an ELR with no definition should result in an "en" label with
        the roleURI as its value
        """
        elr = "ELR"
        self.builder_1.addELR(elr)
        elrPrefix = self.builder_1.roleMap.getPrefix(elr)
        self.assertEqual(
            self.builder_1.taxonomyData.get('roleDefs').get(elrPrefix).get(
                "en"), "ELR Label")

    @patch('arelle.XbrlConst.conceptLabel',
           'http://www.xbrl.org/2003/arcrole/concept-label')
    @patch('arelle.XbrlConst.conceptReference',
           'http://www.xbrl.org/2003/arcrole/concept-reference')
    @patch('arelle.XbrlConst.parentChild',
           'http://www.xbrl.org/2003/arcrole/parent-child')
    @patch('arelle.XbrlConst.summationItem',
           'http://www.xbrl.org/2003/arcrole/summation-item')
    @patch('arelle.XbrlConst.standardLabel',
           'http://www.xbrl.org/2003/role/label')
    @patch('arelle.XbrlConst.documentationLabel',
           'http://www.xbrl.org/2003/role/documentation')
    def test_createViewer(self):
        js_uri = 'ixbrlviewer.js'
        result = self.builder_1.createViewer(js_uri)
        self.assertEqual(len(result.files), 1)
        body = result.files[0].xmlDocument.getroot()[0]
        self.assertEqual(body[0].text, 'BEGIN IXBRL VIEWER EXTENSIONS')
        self.assertEqual(body[1].attrib.get('src'), js_uri)
        self.assertEqual(body[1].attrib.get('type'), 'text/javascript')
        self.assertEqual(body[2].attrib.get('type'),
                         'application/x.ixbrl-viewer+json')
        self.assertEqual(body[3].text, 'END IXBRL VIEWER EXTENSIONS')

    @patch('arelle.XbrlConst.conceptLabel',
           'http://www.xbrl.org/2003/arcrole/concept-label')
    @patch('arelle.XbrlConst.conceptReference',
           'http://www.xbrl.org/2003/arcrole/concept-reference')
    @patch('arelle.XbrlConst.parentChild',
           'http://www.xbrl.org/2003/arcrole/parent-child')
    @patch('arelle.XbrlConst.summationItem',
           'http://www.xbrl.org/2003/arcrole/summation-item')
    @patch('arelle.XbrlConst.standardLabel',
           'http://www.xbrl.org/2003/role/label')
    @patch('arelle.XbrlConst.documentationLabel',
           'http://www.xbrl.org/2003/role/documentation')
    def test_createViewer_bad_path(self):
        js_uri = 'ixbrlviewer.js'
        result = self.builder_2.createViewer(js_uri)
        self.assertEqual(len(result.files), 1)
        body = result.files[0].xmlDocument.getroot()[0]
        self.assertEqual(body[0].text, 'BEGIN IXBRL VIEWER EXTENSIONS')
        self.assertEqual(body[1].attrib.get('src'), js_uri)
        self.assertEqual(body[1].attrib.get('type'), 'text/javascript')
        self.assertEqual(body[2].attrib.get('type'),
                         'application/x.ixbrl-viewer+json')
        self.assertEqual(body[3].text, 'END IXBRL VIEWER EXTENSIONS')