• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hetalang / heta-compiler / 16499499764

24 Jul 2025 02:20PM UTC coverage: 67.552% (+0.1%) from 67.43%
16499499764

push

github

metelkin
draft `getProperty` method for `Top`

1832 of 2849 branches covered (64.3%)

Branch coverage included in aggregate %.

25 of 28 new or added lines in 2 files covered. (89.29%)

26 existing lines in 4 files now uncovered.

3127 of 4492 relevant lines covered (69.61%)

82.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.67
/src/core/component.js
1
const { Top } = require('./top');
1✔
2
const MarkdownIt = require('markdown-it');
1✔
3
const md = new MarkdownIt({html: true, xhtmlOut: false, linkify: true});
1✔
4

5
const { uniqBy, cloneDeep } = require('../utils');
1✔
6
const { ajv } = require('../ajv');
1✔
7
const _get = require('lodash/get');
1✔
8
const _set = require('lodash/set');
1✔
9

10
const schema = {
1✔
11
  type: "object",
12
  description: "Abstract class for all top elements.",
13
  properties: {
14
    class: { type: "string" },
15
    title: {oneOf: [
16
      { type: "null" },
17
      { type: "string" }
18
    ]},
19
    notes: {oneOf: [
20
      { type: "null" },
21
      { type: "string" }
22
    ]},
23
    tags: {oneOf: [
24
      { type: "null" },
25
      { type: "array", "items": { "type": "string" } }
26
    ]},
27
    aux: {oneOf: [
28
      { type: "null" },
29
      {
30
        type: "object",
31
        additionalProperties: {
32
          oneOf: [ { "type": "string" }, { "type": "number" }, {"type": "array"}, { "type": "object" }, { "type": "boolean"} ]
33
        }
34
      }
35
    ]}
36
  }
37
};
38

39
/*
40
  class Component
41

42
  ''' Notes 1 '''
43
  component1 @Component 'title 1' {
44
    tags: [tag1, tag2],
45
    aux: {}
46
  };
47
*/
48
class Component extends Top {
49
  constructor(isCore = false) {
21✔
50
    super(isCore);
377✔
51

52
    this.tags = [];
377✔
53
    this.aux = {};
377✔
54
  }
55
  merge(q = {}) {
×
56
    super.merge(q);
383✔
57

58
    let logger = this._container?.logger;
383✔
59
    let valid = Component.isValid(q, logger);
383✔
60
    
61
    if (valid) {
383✔
62
      if (q.title === null) {
377✔
63
        delete this.title;
2✔
64
      } else if (q.title !== undefined) {
375✔
65
        this.title = q.title;
31✔
66
      }
67
      if (q.notes === null) {
377✔
68
        delete this.notes;
2✔
69
      } else if (q.notes !== undefined) { 
375✔
70
        this.notes = q.notes.trim(); // remove trailing symbols
16✔
71
      }
72
      if (q.tags === null) {
377✔
73
        this.tags = [];
2✔
74
      } else if (q.tags !== undefined) {
375✔
75
        this.tags = q.tags.map((tag) => tag); // clone
19✔
76
      }
77
      if (q.aux === null) {
377✔
78
        this.aux = {};
2✔
79
      } else if (q.aux) {
375✔
80
        this.aux = cloneDeep(q.aux);
18✔
81
      }
82
    }
83
    
84
    return this;
383✔
85
  }
86
  // if NS not set, than undefined
87
  // if set any, than spaceName
88
  // if set nameless, than 'nameless'
89
  get space() {
90
    if (this.namespace) {
367✔
91
      return this.namespace.spaceName;
361✔
92
    } else {
93
      return undefined;
6✔
94
    }
95
  }
96
  static get schemaName(){
97
    return this.prototype.className;
1✔
98
  }
99
  get className(){
100
    return 'Component';
886✔
101
  }
102
  get index(){
103
    if(this.space !== 'nameless'){
165✔
104
      return this.space + '::' + this._id;
125✔
105
    }else{
106
      return this.id;
40✔
107
    }
108
  }
109
  get indexObj(){
UNCOV
110
    return { id: this.id, space: this.space };
×
111
  }
112
  // creates copy of element
113
  clone() {
114
    let componentClone = new this.constructor();
18✔
115
    if (this.title)
18✔
116
      componentClone.title = this.title;
2✔
117
    if (this.notes)
18✔
118
      componentClone.notes = this.notes;
2✔
119
    if (this.tags.length)
18✔
120
      componentClone.tags = this.tags.map(x => x);
3✔
121
    if (this.aux !== undefined)
18!
122
      componentClone.aux = cloneDeep(this.aux);
18✔
123

124
    if (this._isCore)
18!
UNCOV
125
      componentClone._isCore = true;
×
126

127
    return componentClone;
18✔
128
  }
129
  /** Change referencies of component based on suffix/prefix/rename */
130
  updateReferences(_q = {}) {
×
131
    // set defaults
132
    let q = Object.assign({
16✔
133
      prefix: '',
134
      suffix: '',
135
      rename: {}
136
    }, _q);
137

138
    // change references
139
    const iterator = (item, path) => { // Actor { target: 'y', stoichiometry: -1 }, actors[0].target
16✔
140
      let oldRef = _get(this, path);
8✔
141
      let newRef = q.rename[oldRef] 
8✔
142
        || [q.prefix, oldRef, q.suffix].join(''); // default behaviour
143

144
      _set(this, path, newRef);
8✔
145
    };
146
    // search ref in requirements
147
    let req = this.constructor.requirements();
16✔
148
    Object.entries(req)
16✔
149
      .forEach(([prop, rule]) => { // iterates through rules
150
        // isReference: true
151
        if (rule.isReference && this[prop] !== undefined) {
32✔
152
          if (rule.isArray) { // iterates through array
6✔
153
            this[prop].forEach((item, i) => {
5✔
154
              let fullPath = rule.path ? `${prop}[${i}].${rule.path}` : `${prop}[${i}]`;
7!
155
              iterator(item, fullPath, rule);
7✔
156
            });
157
          } else {
158
            let item = this[prop];
1✔
159
            let fullPath = rule.path ? `${prop}.${rule.path}` : `${prop}`;
1!
160
            iterator(item, fullPath, rule);
1✔
161
          }
162
        }
163
      });
164

165
    return this;
16✔
166
  }
167
  get notesHTML() {
168
    if (this.notes === undefined) {
23!
UNCOV
169
      return undefined;
×
170
    }
171
    let renderedOutput = md.render(this.notes)
23✔
172
      .replace(/\n+\r*/g, ' ')
173
      .trim();
174
    return renderedOutput;
23✔
175
  }
176
  static get validate() {
177
    return ajv.compile(schema);
389✔
178
  }
179
  /*
180
    Checking references:
181
    - check properties based on requirements(): required, find by symbol link
182
    - create virtual component if local prop refferences to global component
183
  */
184
  bind(namespace) {
185

186
    let logger = this._container?.logger;
234✔
187
    if (!namespace)
234!
UNCOV
188
      throw new TypeError('"namespace" argument should be set.');
×
189
    
190
    const iterator = (item, path, rule) => {
234✔
191
      let targetId = _get(this, path);
39✔
192
      let target = namespace.get(targetId);
39✔
193

194
      if (!target) {
39✔
195
        logger.error(
3✔
196
          this.index + ` Property "${path}" has lost reference "${targetId}".`,
197
          {type: 'BindingError', space: this.space}
198
        );
199
      } else if(rule.targetClass && !target.instanceOf(rule.targetClass)) {
36✔
200
        logger.error(
3✔
201
          this.index + ` "${path}" property should refer to ${rule.targetClass} but not to ${target.className}.`,
202
          {type: 'BindingError', space: this.space}
203
        );
204
      } else {
205
        // set direct ref
206
        if (rule.setTarget) _set(this, path + 'Obj', target);
33!
207
        // add back references for Record from Process XXX: ugly solution
208
        if (this.instanceOf('Process') && item.className === 'Actor' ){
33✔
209
          target.backReferences.push({
11✔
210
            process: this.id,
211
            _process_: this,
212
            stoichiometry: item.stoichiometry
213
          });
214
        }
215
      }
216
    };
217

218
    // check requirements
219
    let req = this.constructor.requirements();
234✔
220
    Object.entries(req).forEach(([prop, rule]) => { // iterates through rules
234✔
221
      // required: true
222
      if (rule.required && this[prop] === undefined) {
296✔
223
        logger.error(
1✔
224
          `No required "${prop}" property for "${this.index}" of ${this.className}.`,
225
          {type: 'BindingError', space: this.space}
226
        );
227
      }
228
      // isReference: true + className
229
      if (rule.isReference && this[prop] !== undefined) {
296✔
230
        if (rule.isArray) { // iterates through array
39✔
231
          this[prop].forEach((item, i) => {
14✔
232
            let fullPath = rule.path ? `${prop}[${i}].${rule.path}` : `${prop}[${i}]`;
14!
233
            iterator(item, fullPath, rule);
14✔
234
          });
235
        } else {
236
          let item = this[prop];
25✔
237
          let fullPath = rule.path ? `${prop}.${rule.path}` : `${prop}`;
25!
238
          iterator(item, fullPath, rule);
25✔
239
        }
240
      }
241
    });
242
  }
243
  toQ(options = {}) {
2✔
244
    let q = super.toQ(options);
104✔
245
    delete q.action;
104✔
246

247
    q.class = this.className;
104✔
248
    if (this.namespace && this.namespace.spaceName !== 'nameless') q.space = this.space;
104✔
249
    if (this.title) q.title = this.title;
104✔
250
    if (this.notes) q.notes = this.notes;
104✔
251
    if (this.tags.length > 0) q.tags = this.tags.map((tag) => tag);
104✔
252
    if (Object.keys(this.aux).length > 0) q.aux = cloneDeep(this.aux);
104✔
253

254
    return q;
104✔
255
  }
256
  /* recursively create requirements from _requirements, 
257
  currently it is not optimal */
258
  static requirements() { 
259
    if (this.prototype.className === 'Component') {
1,062✔
260
      return this._requirements;
252✔
261
    } else if (this.hasOwnProperty('_requirements')) {
810✔
262
      let proto = Object.getPrototypeOf(this);
137✔
263
      let deeper = this.requirements.call(proto);
137✔
264
      //let deeper = Object.getPrototypeOf(this).requirements();
265
      return Object.assign({}, deeper, this._requirements);
137✔
266
    } else {
267
      let proto = Object.getPrototypeOf(this);
673✔
268
      let deeper = this.requirements.call(proto);
673✔
269
      //let deeper = Object.getPrototypeOf(this).requirements();
270
      return deeper;
673✔
271
    }
272
  }
273
  /* non-unique references */
274
  _references() {
UNCOV
275
    return [];
×
276
  }
277
  /*
278
  array of direct references inside component to another components
279
  it is used inside irt-nav
280
  */
281
  references() {
282
    let nonUnique = this._references();
×
UNCOV
283
    return uniqBy(nonUnique);
×
284
  }
285
}
286

287
Component._requirements = {};
1✔
288

289
module.exports = {
1✔
290
  Component
291
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc