+
diff --git a/bundles/specmate-scheduler/bnd.bnd b/bundles/specmate-scheduler/bnd.bnd
index edd407986..fb1d25461 100644
--- a/bundles/specmate-scheduler/bnd.bnd
+++ b/bundles/specmate-scheduler/bnd.bnd
@@ -1,4 +1,6 @@
Export-Package: \
com.specmate.scheduler,\
com.specmate.scheduler.iterators
--buildpath: specmate-common;version=latest
\ No newline at end of file
+-buildpath: \
+ specmate-common;version=latest,\
+ org.apache.servicemix.bundles.junit
\ No newline at end of file
diff --git a/bundles/specmate-scheduler/src/com/specmate/scheduler/Scheduler.java b/bundles/specmate-scheduler/src/com/specmate/scheduler/Scheduler.java
index ba2e5a9f8..4886caaa8 100644
--- a/bundles/specmate-scheduler/src/com/specmate/scheduler/Scheduler.java
+++ b/bundles/specmate-scheduler/src/com/specmate/scheduler/Scheduler.java
@@ -6,97 +6,104 @@
import com.specmate.scheduler.iterators.ScheduleIterator;
-
/**
- * A facility for threads to schedule recurring tasks for future execution in a background thread.
+ * A facility for threads to schedule recurring tasks for future execution in a
+ * background thread.
*
- * This class is thread-safe: multiple threads can share a single Scheduler
object without the need for external synchronization.
+ * This class is thread-safe: multiple threads can share a single
+ * Scheduler
object without the need for external synchronization.
*
- * Implementation note: internally Scheduler
uses a java.util.Timer
to schedule tasks.
+ * Implementation note: internally Scheduler
uses a
+ * java.util.Timer
to schedule tasks.
*/
public class Scheduler {
- class SchedulerTimerTask extends TimerTask {
-
- private SchedulerTask schedulerTask;
- private ScheduleIterator iterator;
-
- public SchedulerTimerTask(SchedulerTask schedulerTask,
- ScheduleIterator iterator) {
- this.schedulerTask = schedulerTask;
- this.iterator = iterator;
- }
-
- public void run() {
- try {
- schedulerTask.run();
- } finally {
- reschedule(schedulerTask, iterator);
- }
- }
- }
-
- private final Timer timer = new Timer();
-
- public Scheduler() {
- }
-
-/**
- * Terminates this Scheduler
, discarding any currently scheduled tasks. Does not interfere with a currently executing task (if it exists). Once a scheduler has been terminated, its execution thread terminates gracefully, and no more tasks may be scheduled on it.
- *
- * Note that calling this method from within the run method of a scheduler task that was invoked by this scheduler absolutely guarantees that the ongoing task execution is the last task execution that will ever be performed by this scheduler.
- *
- * This method may be called repeatedly; the second and subsequent calls have no effect.
- */
-
- public void cancel() {
- timer.cancel();
- }
-
-/**
- * Schedules the specified task for execution according to the specified schedule. If times specified by the ScheduleIterator
are in the past they are scheduled for immediate execution.
- *
- * @param schedulerTask task to be scheduled
- * @param iterator iterator that describes the schedule
- * @throws IllegalStateException if task was already scheduled or cancelled, scheduler was cancelled, or scheduler thread terminated.
- */
-
- public void schedule(SchedulerTask schedulerTask,
- ScheduleIterator iterator) {
-
- Date time = iterator.next();
- if (time == null) {
- schedulerTask.cancel();
- } else {
- synchronized(schedulerTask.lock) {
- if (schedulerTask.state != SchedulerTask.VIRGIN) {
- throw new IllegalStateException("Task already scheduled " +
- "or cancelled");
- }
- schedulerTask.state = SchedulerTask.SCHEDULED;
- schedulerTask.timerTask =
- new SchedulerTimerTask(schedulerTask, iterator);
- timer.schedule(schedulerTask.timerTask, time);
- }
- }
- }
-
- private void reschedule(SchedulerTask schedulerTask,
- ScheduleIterator iterator) {
-
- Date time = iterator.next();
- if (time == null) {
- schedulerTask.cancel();
- } else {
- synchronized(schedulerTask.lock) {
- if (schedulerTask.state != SchedulerTask.CANCELLED) {
- schedulerTask.timerTask =
- new SchedulerTimerTask(schedulerTask, iterator);
- timer.schedule(schedulerTask.timerTask, time);
- }
- }
- }
- }
+ class SchedulerTimerTask extends TimerTask {
+
+ private SchedulerTask schedulerTask;
+ private ScheduleIterator iterator;
+
+ public SchedulerTimerTask(SchedulerTask schedulerTask, ScheduleIterator iterator) {
+ this.schedulerTask = schedulerTask;
+ this.iterator = iterator;
+ }
+
+ public void run() {
+ try {
+ schedulerTask.run();
+ } finally {
+ reschedule(schedulerTask, iterator);
+ }
+ }
+ }
+
+ private final Timer timer = new Timer();
+
+ public Scheduler() {
+ }
+
+ /**
+ * Terminates this Scheduler
, discarding any currently
+ * scheduled tasks. Does not interfere with a currently executing task (if
+ * it exists). Once a scheduler has been terminated, its execution thread
+ * terminates gracefully, and no more tasks may be scheduled on it.
+ *
+ * Note that calling this method from within the run method of a scheduler
+ * task that was invoked by this scheduler absolutely guarantees that the
+ * ongoing task execution is the last task execution that will ever be
+ * performed by this scheduler.
+ *
+ * This method may be called repeatedly; the second and subsequent calls
+ * have no effect.
+ */
+ public void cancel() {
+ timer.cancel();
+ }
+
+ /**
+ * Schedules the specified task for execution according to the specified
+ * schedule. If times specified by the ScheduleIterator
are in
+ * the past they are scheduled for immediate execution.
+ *
+ *
+ * @param schedulerTask
+ * task to be scheduled
+ * @param iterator
+ * iterator that describes the schedule
+ * @throws IllegalStateException
+ * if task was already scheduled or cancelled, scheduler was
+ * cancelled, or scheduler thread terminated.
+ */
+ public void schedule(SchedulerTask schedulerTask, ScheduleIterator iterator) {
+
+ Date time = iterator.next();
+ if (time == null) {
+ schedulerTask.cancel();
+ } else {
+ synchronized (schedulerTask.lock) {
+ if (schedulerTask.state != SchedulerTask.VIRGIN) {
+ throw new IllegalStateException("Task already scheduled " + "or cancelled");
+ }
+ schedulerTask.state = SchedulerTask.SCHEDULED;
+ schedulerTask.timerTask = new SchedulerTimerTask(schedulerTask, iterator);
+ timer.schedule(schedulerTask.timerTask, time);
+ }
+ }
+ }
+
+ private void reschedule(SchedulerTask schedulerTask, ScheduleIterator iterator) {
+
+ Date time = iterator.next();
+ if (time == null) {
+ schedulerTask.cancel();
+ } else {
+ synchronized (schedulerTask.lock) {
+ if (schedulerTask.state != SchedulerTask.CANCELLED) {
+ schedulerTask.timerTask = new SchedulerTimerTask(schedulerTask, iterator);
+ timer.schedule(schedulerTask.timerTask, time);
+ }
+ }
+ }
+ }
}
-
diff --git a/bundles/specmate-scheduler/src/com/specmate/scheduler/SchedulerIteratorFactory.java b/bundles/specmate-scheduler/src/com/specmate/scheduler/SchedulerIteratorFactory.java
index 40b40b775..746cfd186 100644
--- a/bundles/specmate-scheduler/src/com/specmate/scheduler/SchedulerIteratorFactory.java
+++ b/bundles/specmate-scheduler/src/com/specmate/scheduler/SchedulerIteratorFactory.java
@@ -1,6 +1,8 @@
package com.specmate.scheduler;
import java.util.Arrays;
+import java.util.Date;
+
import com.specmate.common.SpecmateException;
import com.specmate.common.SpecmateValidationException;
import com.specmate.scheduler.iterators.DailyIterator;
@@ -17,24 +19,29 @@ public class SchedulerIteratorFactory {
private static final String DELIM = " ";
public static ScheduleIterator create(String schedule) throws SpecmateException, SpecmateValidationException {
+ return create(schedule, new Date());
+ }
+
+ public static ScheduleIterator create(String schedule, Date date)
+ throws SpecmateException, SpecmateValidationException {
validate(schedule);
schedule = normalizeScheduleString(schedule);
- return constructScheduleIterator(schedule);
+ return constructScheduleIterator(schedule, date);
}
- private static ScheduleIterator constructScheduleIterator(String schedule) throws SpecmateException {
+ private static ScheduleIterator constructScheduleIterator(String schedule, Date date) throws SpecmateException {
String type = getType(schedule);
int[] args = getArgs(schedule);
if (type.equalsIgnoreCase(DAY)) {
- return new DailyIterator(args);
+ return new DailyIterator(date, args);
}
if (type.equalsIgnoreCase(HOUR)) {
- return new HourlyIterator(args);
+ return new HourlyIterator(date, args);
}
if (type.equalsIgnoreCase(MINUTE)) {
- return new MinuteIterator(args);
+ return new MinuteIterator(date, args);
}
throw new SpecmateException("Invalid scheduler type");
}
@@ -92,7 +99,7 @@ private static String[] getArgsStrs(String schedule) {
String[] empty = new String[0];
return empty;
}
- String[] argumentsStr = Arrays.copyOfRange(parts, 1, parts.length - 1);
+ String[] argumentsStr = Arrays.copyOfRange(parts, 1, parts.length);
return argumentsStr;
}
}
diff --git a/bundles/specmate-scheduler/src/com/specmate/scheduler/iterators/DailyIterator.java b/bundles/specmate-scheduler/src/com/specmate/scheduler/iterators/DailyIterator.java
index b566fadcb..ceeb25243 100644
--- a/bundles/specmate-scheduler/src/com/specmate/scheduler/iterators/DailyIterator.java
+++ b/bundles/specmate-scheduler/src/com/specmate/scheduler/iterators/DailyIterator.java
@@ -10,8 +10,8 @@
public class DailyIterator implements ScheduleIterator {
private final Calendar calendar = Calendar.getInstance();
- public DailyIterator(int... time) {
- this(getHourOfDay(time), getMinute(time), getSecond(time), new Date());
+ public DailyIterator(Date date, int... time) {
+ this(getHourOfDay(time), getMinute(time), getSecond(time), date);
}
public DailyIterator(int hourOfDay, int minute, int second, Date date) {
@@ -25,6 +25,7 @@ public DailyIterator(int hourOfDay, int minute, int second, Date date) {
}
}
+ @Override
public Date next() {
calendar.add(Calendar.DATE, 1);
return calendar.getTime();
@@ -33,11 +34,11 @@ public Date next() {
private static int getHourOfDay(int... time) {
return SchedulerUtils.getNumberIfExistsOrZero(0, time);
}
-
+
private static int getMinute(int... time) {
return SchedulerUtils.getNumberIfExistsOrZero(1, time);
}
-
+
private static int getSecond(int... time) {
return SchedulerUtils.getNumberIfExistsOrZero(2, time);
}
diff --git a/bundles/specmate-scheduler/src/com/specmate/scheduler/iterators/HourlyIterator.java b/bundles/specmate-scheduler/src/com/specmate/scheduler/iterators/HourlyIterator.java
index d341aaad8..c5dc3415b 100644
--- a/bundles/specmate-scheduler/src/com/specmate/scheduler/iterators/HourlyIterator.java
+++ b/bundles/specmate-scheduler/src/com/specmate/scheduler/iterators/HourlyIterator.java
@@ -10,8 +10,8 @@
public class HourlyIterator implements ScheduleIterator {
private final Calendar calendar = Calendar.getInstance();
- public HourlyIterator(int... time) {
- this(getMinute(time), getSecond(time), new Date());
+ public HourlyIterator(Date date, int... time) {
+ this(getMinute(time), getSecond(time), date);
}
public HourlyIterator(int minute, int second, Date date) {
@@ -20,10 +20,11 @@ public HourlyIterator(int minute, int second, Date date) {
calendar.set(Calendar.SECOND, second);
calendar.set(Calendar.MILLISECOND, 0);
if (!calendar.getTime().before(date)) {
- calendar.add(Calendar.DATE, -1);
+ calendar.add(Calendar.HOUR_OF_DAY, -1);
}
}
+ @Override
public Date next() {
calendar.add(Calendar.HOUR_OF_DAY, 1);
return calendar.getTime();
@@ -32,7 +33,7 @@ public Date next() {
private static int getMinute(int... time) {
return SchedulerUtils.getNumberIfExistsOrZero(0, time);
}
-
+
private static int getSecond(int... time) {
return SchedulerUtils.getNumberIfExistsOrZero(1, time);
}
diff --git a/bundles/specmate-scheduler/src/com/specmate/scheduler/iterators/MinuteIterator.java b/bundles/specmate-scheduler/src/com/specmate/scheduler/iterators/MinuteIterator.java
index 956ed7b42..d1145e46f 100644
--- a/bundles/specmate-scheduler/src/com/specmate/scheduler/iterators/MinuteIterator.java
+++ b/bundles/specmate-scheduler/src/com/specmate/scheduler/iterators/MinuteIterator.java
@@ -10,8 +10,8 @@
public class MinuteIterator implements ScheduleIterator {
private final Calendar calendar = Calendar.getInstance();
- public MinuteIterator(int... time) {
- this(getSecond(time), new Date());
+ public MinuteIterator(Date date, int... time) {
+ this(getSecond(time), date);
}
public MinuteIterator(int second, Date date) {
@@ -19,10 +19,11 @@ public MinuteIterator(int second, Date date) {
calendar.set(Calendar.SECOND, second);
calendar.set(Calendar.MILLISECOND, 0);
if (!calendar.getTime().before(date)) {
- calendar.add(Calendar.DATE, -1);
+ calendar.add(Calendar.MINUTE, -1);
}
}
+ @Override
public Date next() {
calendar.add(Calendar.MINUTE, 1);
return calendar.getTime();
diff --git a/bundles/specmate-scheduler/src/com/specmate/scheduler/iterators/RestrictedDailyIterator.java b/bundles/specmate-scheduler/src/com/specmate/scheduler/iterators/RestrictedDailyIterator.java
deleted file mode 100644
index d8c3aabc3..000000000
--- a/bundles/specmate-scheduler/src/com/specmate/scheduler/iterators/RestrictedDailyIterator.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.specmate.scheduler.iterators;
-
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Date;
-
-/**
- * A RestrictedDailyIterator
returns a sequence of dates on
- * subsequent days (restricted to a set of days, e.g. weekdays only)
- * representing the same time each day.
- */
-public class RestrictedDailyIterator implements ScheduleIterator {
- private final int[] days;
- private final Calendar calendar = Calendar.getInstance();
-
- public RestrictedDailyIterator(int hourOfDay, int minute, int second, int[] days) {
- this(hourOfDay, minute, second, days, new Date());
- }
-
- public RestrictedDailyIterator(int hourOfDay, int minute, int second, int[] days, Date date) {
- this.days = (int[]) days.clone();
- Arrays.sort(this.days);
-
- calendar.setTime(date);
- calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
- calendar.set(Calendar.MINUTE, minute);
- calendar.set(Calendar.SECOND, second);
- calendar.set(Calendar.MILLISECOND, 0);
- if (!calendar.getTime().before(date)) {
- calendar.add(Calendar.DATE, -1);
- }
- }
-
- public Date next() {
- do {
- calendar.add(Calendar.DATE, 1);
- } while (Arrays.binarySearch(days, calendar.get(Calendar.DAY_OF_WEEK)) < 0);
- return calendar.getTime();
- }
-
-}
diff --git a/bundles/specmate-scheduler/test/com/specmate/schedule/test/IteratorsTest.java b/bundles/specmate-scheduler/test/com/specmate/schedule/test/IteratorsTest.java
new file mode 100644
index 000000000..d74bd4c33
--- /dev/null
+++ b/bundles/specmate-scheduler/test/com/specmate/schedule/test/IteratorsTest.java
@@ -0,0 +1,78 @@
+package com.specmate.schedule.test;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.specmate.scheduler.SchedulerIteratorFactory;
+import com.specmate.scheduler.iterators.ScheduleIterator;
+
+public class IteratorsTest {
+
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+ @Test
+ public void testHourlyIterator() throws Exception {
+ Date date = dateFormat.parse("2018-11-01 09:30:10");
+
+ // scheduled time before current time
+ ScheduleIterator hourlyIterator = SchedulerIteratorFactory.create("hour 20 15", date);
+ Date next = hourlyIterator.next();
+ Assert.assertEquals("2018-11-01 10:20:15", dateFormat.format(next));
+
+ // scheduled time after current time
+ hourlyIterator = SchedulerIteratorFactory.create("hour 40 15", date);
+ next = hourlyIterator.next();
+ Assert.assertEquals("2018-11-01 09:40:15", dateFormat.format(next));
+
+ Date endOfDay = dateFormat.parse("2018-12-31 23:30:10");
+ // scheduled time need day jump
+ hourlyIterator = SchedulerIteratorFactory.create("hour 20 15", endOfDay);
+ next = hourlyIterator.next();
+ Assert.assertEquals("2019-01-01 00:20:15", dateFormat.format(next));
+ }
+
+ @Test
+ public void testMinuteIterator() throws Exception {
+ Date date = dateFormat.parse("2018-11-01 09:30:10");
+
+ // scheduled time before current time
+ ScheduleIterator minuteIterator = SchedulerIteratorFactory.create("minute 15", date);
+ Date next = minuteIterator.next();
+ Assert.assertEquals("2018-11-01 09:30:15", dateFormat.format(next));
+
+ // scheduled time after current time
+ minuteIterator = SchedulerIteratorFactory.create("minute 5", date);
+ next = minuteIterator.next();
+ Assert.assertEquals("2018-11-01 09:31:05", dateFormat.format(next));
+
+ Date endOfDay = dateFormat.parse("2018-12-31 23:59:10");
+ // scheduled time need day jump
+ minuteIterator = SchedulerIteratorFactory.create("minute 5", endOfDay);
+ next = minuteIterator.next();
+ Assert.assertEquals("2019-01-01 00:00:05", dateFormat.format(next));
+ }
+
+ @Test
+ public void testDaily() throws Exception {
+ Date date = dateFormat.parse("2018-11-01 09:30:10");
+
+ // scheduled time before current time
+ ScheduleIterator dayIterator = SchedulerIteratorFactory.create("day 8 20 5", date);
+ Date next = dayIterator.next();
+ Assert.assertEquals("2018-11-02 08:20:05", dateFormat.format(next));
+
+ // scheduled time after current time
+ dayIterator = SchedulerIteratorFactory.create("day 10 20 5", date);
+ next = dayIterator.next();
+ Assert.assertEquals("2018-11-01 10:20:05", dateFormat.format(next));
+
+ Date endOfDay = dateFormat.parse("2018-12-31 23:59:10");
+ // scheduled minutes need year jump
+ dayIterator = SchedulerIteratorFactory.create("day 10 20 5", endOfDay);
+ next = dayIterator.next();
+ Assert.assertEquals("2019-01-01 10:20:05", dateFormat.format(next));
+ }
+}
diff --git a/bundles/specmate-std-env/dev-specmate-all.bndrun b/bundles/specmate-std-env/dev-specmate-all.bndrun
index ddc1f26d9..bdf6e6e40 100644
--- a/bundles/specmate-std-env/dev-specmate-all.bndrun
+++ b/bundles/specmate-std-env/dev-specmate-all.bndrun
@@ -187,6 +187,7 @@
com.sun.jersey.jersey-server;version='[1.19.0,1.19.1)',\
javax.ws.rs.jsr311-api;version='[1.1.1,1.1.2)',\
specmate-rest;version=snapshot,\
+ org.apache.commons.collections4;version='[4.0.0,4.0.1)',\
specmate-scheduler;version=snapshot
-runproperties: \
@@ -198,4 +199,5 @@
-runrepos: \
Workspace,\
Local
--runvm: -Xmx6000m, -Djdk.crypto.KeyAgreement.legacyKDF=true
\ No newline at end of file
+-runvm: -Xmx6000m\n\
+ -Djdk.crypto.KeyAgreement.legacyKDF=true
\ No newline at end of file
diff --git a/bundles/specmate-std-env/prod-specmate-all.bndrun b/bundles/specmate-std-env/prod-specmate-all.bndrun
index 06154ee49..c8b7082e3 100644
--- a/bundles/specmate-std-env/prod-specmate-all.bndrun
+++ b/bundles/specmate-std-env/prod-specmate-all.bndrun
@@ -172,6 +172,7 @@
org.apache.commons.lang3;version='[3.3.2,3.3.3)',\
org.eclipse.emf.cdo.server.db;version='[4.4.0,4.4.1)',\
specmate-rest;version=snapshot,\
+ org.apache.commons.collections4;version='[4.0.0,4.0.1)',\
specmate-scheduler;version=snapshot
-runproperties: \
diff --git a/bundles/specmate-testspecification/bnd.bnd b/bundles/specmate-testspecification/bnd.bnd
index b897e0901..fc5b2f92e 100644
--- a/bundles/specmate-testspecification/bnd.bnd
+++ b/bundles/specmate-testspecification/bnd.bnd
@@ -17,4 +17,6 @@
org.sat4j.pb,\
specmate-emfrest-api;version=latest,\
specmate-rest;version=latest
-Private-Package: com.specmate.testspecification.internal.services
\ No newline at end of file
+Private-Package: \
+ com.specmate.testspecification.internal.services,\
+ com.specmate.testspecification.internal.testskeleton
\ No newline at end of file
diff --git a/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/services/CEGTestCaseGenerator.java b/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/services/CEGTestCaseGenerator.java
index 434fcb85b..537087667 100644
--- a/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/services/CEGTestCaseGenerator.java
+++ b/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/services/CEGTestCaseGenerator.java
@@ -1,6 +1,7 @@
package com.specmate.testspecification.internal.services;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -8,6 +9,7 @@
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
+import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
@@ -65,7 +67,7 @@ protected void generateParameters() {
/**
* Determines if a node is an input, output or intermediate node.
- *
+ *
* @param node
* @return ParameterType.INPUT, if the nodes is an input node,
* ParameterType.OUTPUT, if the node is an output node,
@@ -143,6 +145,7 @@ private TestCase createTestCase(NodeEvaluation evaluation, TestSpecification spe
String parameterValue = StringUtils.join(constraints, ",");
ParameterAssignment assignment = TestspecificationFactory.eINSTANCE.createParameterAssignment();
assignment.setId(SpecmateEcoreUtil.getIdForChild(testCase, assignment.eClass()));
+ assignment.setName(assignment.getId());
assignment.setParameter(parameter);
assignment.setValue(parameterValue);
assignment.setCondition(parameterValue);
@@ -168,10 +171,10 @@ private String negateCondition(String condition) {
}
/**
- * Node evaluations are a precursor to test cases. This method computes the
- * node evaluations according to the rules in the Specmate systems
- * requirements documentation.
- *
+ * Node evaluations are a precursor to test cases. This method computes the node
+ * evaluations according to the rules in the Specmate systems requirements
+ * documentation.
+ *
* @param nodes
* @return
* @throws SpecmateException
@@ -211,8 +214,8 @@ private Pair, Set> refineEvaluations(Set getInitialEvaluations() {
Set evaluations = new HashSet<>();
@@ -247,8 +250,8 @@ private Optional getAnyIntermediateNode(NodeEvaluation evaluation) {
}
/**
- * Returns evaluations that have intermediate nodes (i.e. nodes that have to
- * be evaluated)
+ * Returns evaluations that have intermediate nodes (i.e. nodes that have to be
+ * evaluated)
*/
private Set getIntermediateEvaluations(Set evaluations) {
HashSet intermediate = new HashSet<>();
@@ -322,9 +325,9 @@ private void handleAllCase(boolean isAnd, NodeEvaluation evaluation, IModelNode
}
/**
- * Sets the value of a node in an evaluation but checks first if it is
- * already set with a different value
- *
+ * Sets the value of a node in an evaluation but checks first if it is already
+ * set with a different value
+ *
* @return false if an inconsistent value would be set in the node
*/
private boolean checkAndSet(NodeEvaluation evaluation, CEGNode node, TaggedBoolean effectiveValue)
@@ -338,9 +341,9 @@ private boolean checkAndSet(NodeEvaluation evaluation, CEGNode node, TaggedBoole
}
/**
- * Runs through the list of evaluations and merges the ones that can be
- * merged. Identifiey inconsistent evaluations
- *
+ * Runs through the list of evaluations and merges the ones that can be merged.
+ * Identify inconsistent evaluations
+ *
* @throws SpecmateException
*/
private Pair, Set> mergeCompatibleEvaluations(Set evaluations)
@@ -441,10 +444,11 @@ private int getAdditionalVar(int i) {
return nodes.size() + i;
}
- private IVecInt getVectorForVariables(int... vars) {
+ private IVecInt getVectorForVariables(Integer... vars) {
IVecInt vector = new VecInt(vars.length + 1);
- for (int i = 0; i < vars.length; i++)
+ for (int i = 0; i < vars.length; i++) {
vector.push(vars[i]);
+ }
return vector;
}
@@ -475,8 +479,8 @@ private NodeEvaluation fill(NodeEvaluation evaluation) throws SpecmateException
}
/**
- * Sets the value in an evaluation based on an original evaluation and a
- * model value.
+ * Sets the value in an evaluation based on an original evaluation and a model
+ * value.
*/
private void setModelValue(NodeEvaluation originalEvaluation, NodeEvaluation targetEvaluation, int varNameValue) {
boolean value = varNameValue > 0;
@@ -516,6 +520,7 @@ private void pushEvaluation(NodeEvaluation evaluation, GateTranslator translator
}
}
+ /** Feeds constraints representing the CEG structure to the solver */
private void pushCEGStructure(GateTranslator translator) throws ContradictionException {
for (IModelNode node : nodes) {
int varForNode = getVarForNode(node);
@@ -528,6 +533,35 @@ private void pushCEGStructure(GateTranslator translator) throws ContradictionExc
}
}
}
+ pushMutualExclusiveConstraints(translator);
+ }
+
+ private void pushMutualExclusiveConstraints(GateTranslator translator) throws ContradictionException {
+ Collection> mutualExclusiveNodeSets = getMutualExclusiveNodeSets();
+ for (Collection mutexSet : mutualExclusiveNodeSets) {
+ Integer[] variables = mutexSet.stream().map(node -> getVarForNode(node)).collect(Collectors.toList())
+ .toArray(new Integer[0]);
+ translator.addAtMost(getVectorForVariables(variables), 1);
+ }
+ }
+
+ private Collection> getMutualExclusiveNodeSets() {
+ Collection> result = new ArrayList<>();
+ Map> multiMap = new HashMap>();
+ for (IModelNode node : nodes) {
+ CEGNode cegNode = (CEGNode) node;
+ if (cegNode.getCondition().trim().startsWith("=")) {
+ String variable = cegNode.getVariable();
+ if(!multiMap.containsKey(variable)) {
+ multiMap.put(variable, new HashSet());
+ }
+ multiMap.get(variable).add(cegNode);
+ }
+ }
+ for (String key : multiMap.keySet()) {
+ result.add(multiMap.get(key));
+ }
+ return result;
}
/** Returns the CEG node for a given variable (given as int) */
@@ -545,7 +579,7 @@ private IVecInt getPredecessorVector(IModelNode node) {
IVecInt vector = new VecInt();
for (IModelConnection conn : node.getIncomingConnections()) {
IModelNode pre = conn.getSource();
- int var = getVarForNode((CEGNode) pre);
+ int var = getVarForNode(pre);
if (((CEGConnection) conn).isNegate()) {
var *= -1;
}
@@ -555,9 +589,9 @@ private IVecInt getPredecessorVector(IModelNode node) {
}
/**
- * Equality checker that ignores differences in the fields id, name and
- * position
+ * Equality checker that ignores differences in the fields id, name and position
*/
+ @SuppressWarnings("serial")
private class IdNamePositionIgnoreEqualityHelper extends EqualityHelper {
@Override
protected boolean haveEqualFeature(EObject eObject1, EObject eObject2, EStructuralFeature feature) {
diff --git a/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/services/TestGeneratorService.java b/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/services/TestGeneratorService.java
index 1aa3305d5..cff0392b2 100644
--- a/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/services/TestGeneratorService.java
+++ b/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/services/TestGeneratorService.java
@@ -6,7 +6,6 @@
import org.osgi.service.component.annotations.Component;
import com.specmate.common.SpecmateException;
-import com.specmate.common.SpecmateValidationException;
import com.specmate.emfrest.api.IRestService;
import com.specmate.emfrest.api.RestServiceBase;
import com.specmate.model.processes.Process;
@@ -38,8 +37,7 @@ public boolean canPost(Object target, Object object) {
/** {@inheritDoc} */
@Override
- public RestResult> post(Object target, Object object, String token)
- throws SpecmateValidationException, SpecmateException {
+ public RestResult> post(Object target, Object object, String token) throws SpecmateException {
TestSpecification specification = (TestSpecification) target;
EObject container = specification.eContainer();
if (container instanceof CEGModel) {
@@ -47,7 +45,7 @@ public RestResult> post(Object target, Object object, String token)
} else if (container instanceof Process) {
new ProcessTestCaseGenerator(specification).generate();
} else {
- throw new SpecmateValidationException(
+ throw new SpecmateException(
"You can only generate test cases from ceg models or processes. The supplied element is of class "
+ container.getClass().getSimpleName());
}
diff --git a/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/services/TestSkeletonGeneratorService.java b/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/services/TestSkeletonGeneratorService.java
new file mode 100644
index 000000000..40588f121
--- /dev/null
+++ b/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/services/TestSkeletonGeneratorService.java
@@ -0,0 +1,69 @@
+package com.specmate.testspecification.internal.services;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+
+import com.specmate.common.SpecmateException;
+import com.specmate.emfrest.api.IRestService;
+import com.specmate.emfrest.api.RestServiceBase;
+import com.specmate.model.testspecification.TestSpecification;
+import com.specmate.model.testspecification.TestSpecificationSkeleton;
+import com.specmate.rest.RestResult;
+import com.specmate.testspecification.internal.testskeleton.BaseSkeleton;
+import com.specmate.testspecification.internal.testskeleton.JavaTestSpecificationSkeleton;
+import com.specmate.testspecification.internal.testskeleton.JavascriptTestSpecificationSkeleton;
+
+@Component(immediate = true, service = IRestService.class)
+public class TestSkeletonGeneratorService extends RestServiceBase {
+ private final String LPARAM = "language";
+ private final String JAVA = "java";
+ private final String JAVASCRIPT = "javascript";
+ private Map skeletonGenerators;
+
+ @Activate
+ public void activate() {
+ skeletonGenerators = new HashMap<>();
+ skeletonGenerators.put(JAVA, new JavaTestSpecificationSkeleton(JAVA));
+ skeletonGenerators.put(JAVASCRIPT, new JavascriptTestSpecificationSkeleton(JAVASCRIPT));
+ }
+
+ @Override
+ public String getServiceName() {
+ return "generateTestSkeleton";
+ }
+
+ @Override
+ public boolean canGet(Object object) {
+ return object instanceof TestSpecification;
+ }
+
+ @Override
+ public RestResult> get(Object object, MultivaluedMap queryParams, String token)
+ throws SpecmateException {
+
+ String language = queryParams.getFirst(LPARAM);
+ if (language == null) {
+ return new RestResult<>(Response.Status.BAD_REQUEST);
+ }
+
+ BaseSkeleton generator = skeletonGenerators.get(language);
+ if (generator == null) {
+ return new RestResult<>(Response.Status.BAD_REQUEST);
+ }
+
+ if (!(object instanceof TestSpecification)) {
+ return new RestResult<>(Response.Status.BAD_REQUEST);
+ }
+
+ TestSpecification ts = (TestSpecification) object;
+
+ return new RestResult(Response.Status.OK, generator.generate(ts));
+ }
+
+}
diff --git a/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/testskeleton/BaseSkeleton.java b/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/testskeleton/BaseSkeleton.java
new file mode 100644
index 000000000..84fb31509
--- /dev/null
+++ b/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/testskeleton/BaseSkeleton.java
@@ -0,0 +1,82 @@
+package com.specmate.testspecification.internal.testskeleton;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import com.specmate.model.support.util.SpecmateEcoreUtil;
+import com.specmate.model.testspecification.ParameterAssignment;
+import com.specmate.model.testspecification.ParameterType;
+import com.specmate.model.testspecification.TestCase;
+import com.specmate.model.testspecification.TestSpecification;
+import com.specmate.model.testspecification.TestSpecificationSkeleton;
+import com.specmate.model.testspecification.TestspecificationFactory;
+
+public abstract class BaseSkeleton {
+ private static Pattern startsNumerical = Pattern.compile("^[0-9]");
+ private static Pattern invalidChars = Pattern.compile("[^a-zA-Z_0-9\\_]");
+ protected String language;
+ protected String testArea;
+ protected Date generationDate;
+ protected TestSpecification testSpecification;
+
+ public BaseSkeleton(String language) {
+ this.language = language;
+ this.generationDate = new Date();
+ }
+
+ public TestSpecificationSkeleton generate(TestSpecification testSpecification) {
+ StringBuilder sb = new StringBuilder();
+ this.testSpecification = testSpecification;
+ this.testArea = testSpecification.getName();
+ TestSpecificationSkeleton tss = TestspecificationFactory.eINSTANCE.createTestSpecificationSkeleton();
+ tss.setLanguage(language);
+ tss.setName(generateFileName());
+ tss.setCode(generateCode(sb));
+
+ return tss;
+ }
+
+ protected void appendTestCaseMethodName(StringBuilder sb, TestCase tc) {
+ List pAssignments = SpecmateEcoreUtil.pickInstancesOf(tc.getContents(),
+ ParameterAssignment.class);
+
+ ParameterAssignment output = null;
+ for (ParameterAssignment pAssignment : pAssignments) {
+ if (pAssignment.getParameter().getType().equals(ParameterType.OUTPUT)) {
+ output = pAssignment;
+ } else {
+ appendParameterValue(sb, pAssignment);
+ }
+ }
+
+ if (output != null) {
+ appendParameterValue(sb, output);
+ }
+ }
+
+ protected void appendDateComment(StringBuilder sb) {
+ sb.append("/*\n");
+ sb.append(" * Datum: ");
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+ sb.append(sdf.format(generationDate));
+ sb.append("\n */\n\n");
+ }
+
+ protected String replaceInvalidChars(String r) {
+ r = startsNumerical.matcher(r).replaceAll("");
+ return invalidChars.matcher(r).replaceAll("_");
+ }
+
+ private void appendParameterValue(StringBuilder sb, ParameterAssignment pa) {
+ sb.append("___");
+ sb.append(replaceInvalidChars(pa.getParameter().getName()));
+ sb.append("__");
+ sb.append(replaceInvalidChars(pa.getValue()));
+ }
+
+ protected abstract String generateCode(StringBuilder sb);
+
+ protected abstract String generateFileName();
+}
diff --git a/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/testskeleton/JavaTestSpecificationSkeleton.java b/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/testskeleton/JavaTestSpecificationSkeleton.java
new file mode 100644
index 000000000..4aeb5d440
--- /dev/null
+++ b/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/testskeleton/JavaTestSpecificationSkeleton.java
@@ -0,0 +1,50 @@
+package com.specmate.testspecification.internal.testskeleton;
+
+import java.util.List;
+
+import com.specmate.model.support.util.SpecmateEcoreUtil;
+import com.specmate.model.testspecification.TestCase;
+
+public class JavaTestSpecificationSkeleton extends BaseSkeleton {
+
+ public JavaTestSpecificationSkeleton(String language) {
+ super(language);
+ }
+
+ @Override
+ protected String generateCode(StringBuilder sb) {
+ sb.append("import org.junit.Test;\n");
+ sb.append("import org.junit.Assert;\n\n");
+ appendDateComment(sb);
+ sb.append("public class ");
+ sb.append(generateClassname());
+ sb.append(" {\n\n");
+ List testCases = SpecmateEcoreUtil.pickInstancesOf(testSpecification.getContents(), TestCase.class);
+ for (TestCase tc : testCases) {
+ sb.append("\t/*\n");
+ sb.append("\t * Testfall: ");
+ sb.append(tc.getName());
+ sb.append("\n\t */\n");
+ sb.append("\t@Test\n");
+ sb.append("\tpublic void ");
+ sb.append(generateClassname());
+ appendTestCaseMethodName(sb, tc);
+ sb.append("() {\n");
+ sb.append("\t\tAssert.throw();\n");
+ sb.append("\t}\n\n");
+ }
+
+ sb.append("}");
+
+ return sb.toString();
+ }
+
+ @Override
+ protected String generateFileName() {
+ return generateClassname() + ".java";
+ }
+
+ private String generateClassname() {
+ return replaceInvalidChars(testArea) + "Test";
+ }
+}
diff --git a/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/testskeleton/JavascriptTestSpecificationSkeleton.java b/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/testskeleton/JavascriptTestSpecificationSkeleton.java
new file mode 100644
index 000000000..660256d6e
--- /dev/null
+++ b/bundles/specmate-testspecification/src/com/specmate/testspecification/internal/testskeleton/JavascriptTestSpecificationSkeleton.java
@@ -0,0 +1,43 @@
+package com.specmate.testspecification.internal.testskeleton;
+
+import java.util.List;
+
+import com.specmate.model.support.util.SpecmateEcoreUtil;
+import com.specmate.model.testspecification.TestCase;
+
+public class JavascriptTestSpecificationSkeleton extends BaseSkeleton {
+
+ public JavascriptTestSpecificationSkeleton(String language) {
+ super(language);
+ }
+
+ @Override
+ protected String generateCode(StringBuilder sb) {
+ appendDateComment(sb);
+ sb.append("describe('");
+ sb.append(replaceInvalidChars(testArea));
+ sb.append("', () => {\n\n");
+ List testCases = SpecmateEcoreUtil.pickInstancesOf(testSpecification.getContents(), TestCase.class);
+ for (TestCase tc : testCases) {
+ sb.append("\t/*\n");
+ sb.append("\t * Testfall: ");
+ sb.append(tc.getName());
+ sb.append("\n\t */\n");
+ sb.append("\tit('");
+ sb.append(replaceInvalidChars(testArea));
+ appendTestCaseMethodName(sb, tc);
+ sb.append("', () => {\n");
+ sb.append("\t\tthrow new Error('not implemented yet');\n");
+ sb.append("\t});\n\n");
+ }
+
+ sb.append("});");
+
+ return sb.toString();
+ }
+
+ @Override
+ protected String generateFileName() {
+ return replaceInvalidChars(testArea) + ".js";
+ }
+}
diff --git a/bundles/specmate-trello-connector/src/com/specmate/connectors/trello/internal/services/TrelloConnector.java b/bundles/specmate-trello-connector/src/com/specmate/connectors/trello/internal/services/TrelloConnector.java
index 960748f56..5dbb51842 100644
--- a/bundles/specmate-trello-connector/src/com/specmate/connectors/trello/internal/services/TrelloConnector.java
+++ b/bundles/specmate-trello-connector/src/com/specmate/connectors/trello/internal/services/TrelloConnector.java
@@ -17,7 +17,6 @@
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.log.LogService;
-import com.specmate.rest.RestClient;
import com.specmate.common.SpecmateException;
import com.specmate.common.SpecmateValidationException;
import com.specmate.connectors.api.IRequirementsSource;
@@ -28,6 +27,7 @@
import com.specmate.model.base.IContainer;
import com.specmate.model.requirements.Requirement;
import com.specmate.model.requirements.RequirementsFactory;
+import com.specmate.rest.RestClient;
import com.specmate.rest.RestResult;
@Component(immediate = true, service = IRequirementsSource.class, configurationPid = TrelloConnectorConfig.PID, configurationPolicy = ConfigurationPolicy.REQUIRE)
@@ -78,6 +78,7 @@ public Collection getRequirements() throws SpecmateException {
RestResult restResult = restClient.getList("/1/boards/" + boardId + "/cards", "key", this.key,
"token", this.token);
if (restResult.getResponse().getStatus() == Status.OK.getStatusCode()) {
+ restResult.getResponse().close();
List requirements = new ArrayList<>();
JSONArray cardsArray = restResult.getPayload();
for (int i = 0; i < cardsArray.length(); i++) {
@@ -86,6 +87,7 @@ public Collection getRequirements() throws SpecmateException {
}
return requirements;
} else {
+ restResult.getResponse().close();
throw new SpecmateException("Could not retrieve list of trello cards.");
}
}
@@ -106,6 +108,7 @@ public List getLists() throws SpecmateException {
RestResult restResult = restClient.getList("/1/boards/" + boardId + "/lists", "cards", "open", "key",
this.key, "token", this.token);
if (restResult.getResponse().getStatus() == Status.OK.getStatusCode()) {
+ restResult.getResponse().close();
List folders = new ArrayList<>();
JSONArray listsArray = restResult.getPayload();
for (int i = 0; i < listsArray.length(); i++) {
@@ -115,6 +118,7 @@ public List getLists() throws SpecmateException {
}
return folders;
}
+ restResult.getResponse().close();
throw new SpecmateException("Could not load Trello Lists.");
}
diff --git a/web/package.json b/web/package.json
index 9b29cdb82..c00d7734f 100644
--- a/web/package.json
+++ b/web/package.json
@@ -38,6 +38,7 @@
"bootstrap": "4.0.0",
"classlist.js": "1.1.20150312",
"core-js": "2.5.3",
+ "file-saver": "^2.0.0-rc.4",
"flag-icon-css": "2.9.0",
"font-awesome": "4.7.0",
"jquery": "3.3.1",
@@ -53,6 +54,7 @@
},
"devDependencies": {
"@biesbjerg/ngx-translate-extract": "2.3.4",
+ "@types/file-saver": "^2.0.0",
"@types/yargs": "^11.0.0",
"angular2-dependencies-graph": "1.0.0-alpha.12",
"angular2-template-loader": "0.6.2",
diff --git a/web/src/app/factory/util/graph-element-factory-selector.ts b/web/src/app/factory/util/graph-element-factory-selector.ts
index 99490d750..b0df1ffb0 100644
--- a/web/src/app/factory/util/graph-element-factory-selector.ts
+++ b/web/src/app/factory/util/graph-element-factory-selector.ts
@@ -20,7 +20,7 @@ import { ProcessConnection } from '../../model/ProcessConnection';
import { ProcessConnectionFactory } from '../process-connection-factory';
import { IModelNode } from '../../model/IModelNode';
-type Coords = {x: number, y: number};
+export type Coords = {x: number, y: number};
export class GraphElementFactorySelector {
public static getNodeFactory(template: IContainer, coords: Coords, dataService: SpecmateDataService):
PositionableElementFactoryBase {
diff --git a/web/src/app/model/BatchOperation.ts b/web/src/app/model/BatchOperation.ts
index 1320748bd..da93a9c01 100644
--- a/web/src/app/model/BatchOperation.ts
+++ b/web/src/app/model/BatchOperation.ts
@@ -5,7 +5,7 @@
export class BatchOperation {
- ___nsuri: string = "http://specmate.com/20180925/model/batch";
+ ___nsuri: string = "http://specmate.com/20181108/model/batch";
public url: string;
public className: string = "BatchOperation";
public static className: string = "BatchOperation";
diff --git a/web/src/app/model/CEGConnection.ts b/web/src/app/model/CEGConnection.ts
index 4d3a88f5b..9856243dc 100644
--- a/web/src/app/model/CEGConnection.ts
+++ b/web/src/app/model/CEGConnection.ts
@@ -4,7 +4,7 @@
export class CEGConnection {
- ___nsuri: string = "http://specmate.com/20180925/model/requirements";
+ ___nsuri: string = "http://specmate.com/20181108/model/requirements";
public url: string;
public className: string = "CEGConnection";
public static className: string = "CEGConnection";
diff --git a/web/src/app/model/CEGModel.ts b/web/src/app/model/CEGModel.ts
index d8155ca3e..83b1b51d7 100644
--- a/web/src/app/model/CEGModel.ts
+++ b/web/src/app/model/CEGModel.ts
@@ -4,7 +4,7 @@
export class CEGModel {
- ___nsuri: string = "http://specmate.com/20180925/model/requirements";
+ ___nsuri: string = "http://specmate.com/20181108/model/requirements";
public url: string;
public className: string = "CEGModel";
public static className: string = "CEGModel";
diff --git a/web/src/app/model/CEGNode.ts b/web/src/app/model/CEGNode.ts
index 09ba8be39..f87623257 100644
--- a/web/src/app/model/CEGNode.ts
+++ b/web/src/app/model/CEGNode.ts
@@ -4,7 +4,7 @@
export class CEGNode {
- ___nsuri: string = "http://specmate.com/20180925/model/requirements";
+ ___nsuri: string = "http://specmate.com/20181108/model/requirements";
public url: string;
public className: string = "CEGNode";
public static className: string = "CEGNode";
diff --git a/web/src/app/model/Change.ts b/web/src/app/model/Change.ts
index b5d211533..271ed0342 100644
--- a/web/src/app/model/Change.ts
+++ b/web/src/app/model/Change.ts
@@ -4,7 +4,7 @@
export class Change {
- ___nsuri: string = "http://specmate.com/20180925/model/history";
+ ___nsuri: string = "http://specmate.com/20181108/model/history";
public url: string;
public className: string = "Change";
public static className: string = "Change";
diff --git a/web/src/app/model/Folder.ts b/web/src/app/model/Folder.ts
index 8a4c1f3e5..6e8431d47 100644
--- a/web/src/app/model/Folder.ts
+++ b/web/src/app/model/Folder.ts
@@ -4,7 +4,7 @@
export class Folder {
- ___nsuri: string = "http://specmate.com/20180925/model/base";
+ ___nsuri: string = "http://specmate.com/20181108/model/base";
public url: string;
public className: string = "Folder";
public static className: string = "Folder";
diff --git a/web/src/app/model/History.ts b/web/src/app/model/History.ts
index 3966decd2..f399c65a4 100644
--- a/web/src/app/model/History.ts
+++ b/web/src/app/model/History.ts
@@ -5,7 +5,7 @@
export class History {
- ___nsuri: string = "http://specmate.com/20180925/model/history";
+ ___nsuri: string = "http://specmate.com/20181108/model/history";
public url: string;
public className: string = "History";
public static className: string = "History";
diff --git a/web/src/app/model/HistoryEntry.ts b/web/src/app/model/HistoryEntry.ts
index 5a2850add..cbe17494c 100644
--- a/web/src/app/model/HistoryEntry.ts
+++ b/web/src/app/model/HistoryEntry.ts
@@ -5,7 +5,7 @@
export class HistoryEntry {
- ___nsuri: string = "http://specmate.com/20180925/model/history";
+ ___nsuri: string = "http://specmate.com/20181108/model/history";
public url: string;
public className: string = "HistoryEntry";
public static className: string = "HistoryEntry";
@@ -17,7 +17,7 @@
public comment: EString;
// References
-
+
// Containment
public changes: Change[];
diff --git a/web/src/app/model/IExternal.ts b/web/src/app/model/IExternal.ts
index 7a37f455e..91862ce9d 100644
--- a/web/src/app/model/IExternal.ts
+++ b/web/src/app/model/IExternal.ts
@@ -4,7 +4,7 @@
export class IExternal {
- ___nsuri: string = "http://specmate.com/20180925/model/base";
+ ___nsuri: string = "http://specmate.com/20181108/model/base";
public url: string;
public className: string = "IExternal";
public static className: string = "IExternal";
diff --git a/web/src/app/model/IModelConnection.ts b/web/src/app/model/IModelConnection.ts
index 1bf42ca42..2846eff66 100644
--- a/web/src/app/model/IModelConnection.ts
+++ b/web/src/app/model/IModelConnection.ts
@@ -4,7 +4,7 @@
export class IModelConnection {
- ___nsuri: string = "http://specmate.com/20180925/model/base";
+ ___nsuri: string = "http://specmate.com/20181108/model/base";
public url: string;
public className: string = "IModelConnection";
public static className: string = "IModelConnection";
diff --git a/web/src/app/model/IModelNode.ts b/web/src/app/model/IModelNode.ts
index 19dafab59..84cbc2f54 100644
--- a/web/src/app/model/IModelNode.ts
+++ b/web/src/app/model/IModelNode.ts
@@ -4,7 +4,7 @@
export class IModelNode {
- ___nsuri: string = "http://specmate.com/20180925/model/base";
+ ___nsuri: string = "http://specmate.com/20181108/model/base";
public url: string;
public className: string = "IModelNode";
public static className: string = "IModelNode";
diff --git a/web/src/app/model/ITracingElement.ts b/web/src/app/model/ITracingElement.ts
index 2bc407f5b..337329d01 100644
--- a/web/src/app/model/ITracingElement.ts
+++ b/web/src/app/model/ITracingElement.ts
@@ -4,7 +4,7 @@
export class ITracingElement {
- ___nsuri: string = "http://specmate.com/20180925/model/base";
+ ___nsuri: string = "http://specmate.com/20181108/model/base";
public url: string;
public className: string = "ITracingElement";
public static className: string = "ITracingElement";
diff --git a/web/src/app/model/Operation.ts b/web/src/app/model/Operation.ts
index 6d62826c8..12cd9712b 100644
--- a/web/src/app/model/Operation.ts
+++ b/web/src/app/model/Operation.ts
@@ -5,7 +5,7 @@
export class Operation {
- ___nsuri: string = "http://specmate.com/20180925/model/batch";
+ ___nsuri: string = "http://specmate.com/20181108/model/batch";
public url: string;
public className: string = "Operation";
public static className: string = "Operation";
diff --git a/web/src/app/model/ParameterAssignment.ts b/web/src/app/model/ParameterAssignment.ts
index f26df2d42..638cbcc70 100644
--- a/web/src/app/model/ParameterAssignment.ts
+++ b/web/src/app/model/ParameterAssignment.ts
@@ -4,7 +4,7 @@
export class ParameterAssignment {
- ___nsuri: string = "http://specmate.com/20180925/model/testspecification";
+ ___nsuri: string = "http://specmate.com/20181108/model/testspecification";
public url: string;
public className: string = "ParameterAssignment";
public static className: string = "ParameterAssignment";
diff --git a/web/src/app/model/Process.ts b/web/src/app/model/Process.ts
index 6f168160f..eb5bf4b09 100644
--- a/web/src/app/model/Process.ts
+++ b/web/src/app/model/Process.ts
@@ -4,7 +4,7 @@
export class Process {
- ___nsuri: string = "http://specmate.com/20180925/model/processes";
+ ___nsuri: string = "http://specmate.com/20181108/model/processes";
public url: string;
public className: string = "Process";
public static className: string = "Process";
diff --git a/web/src/app/model/ProcessConnection.ts b/web/src/app/model/ProcessConnection.ts
index 9761711f5..e5a968c50 100644
--- a/web/src/app/model/ProcessConnection.ts
+++ b/web/src/app/model/ProcessConnection.ts
@@ -4,7 +4,7 @@
export class ProcessConnection {
- ___nsuri: string = "http://specmate.com/20180925/model/processes";
+ ___nsuri: string = "http://specmate.com/20181108/model/processes";
public url: string;
public className: string = "ProcessConnection";
public static className: string = "ProcessConnection";
diff --git a/web/src/app/model/ProcessDecision.ts b/web/src/app/model/ProcessDecision.ts
index 4fe298ca8..03ab16a14 100644
--- a/web/src/app/model/ProcessDecision.ts
+++ b/web/src/app/model/ProcessDecision.ts
@@ -4,7 +4,7 @@
export class ProcessDecision {
- ___nsuri: string = "http://specmate.com/20180925/model/processes";
+ ___nsuri: string = "http://specmate.com/20181108/model/processes";
public url: string;
public className: string = "ProcessDecision";
public static className: string = "ProcessDecision";
diff --git a/web/src/app/model/ProcessEnd.ts b/web/src/app/model/ProcessEnd.ts
index fc78a3aa6..75ef43941 100644
--- a/web/src/app/model/ProcessEnd.ts
+++ b/web/src/app/model/ProcessEnd.ts
@@ -4,7 +4,7 @@
export class ProcessEnd {
- ___nsuri: string = "http://specmate.com/20180925/model/processes";
+ ___nsuri: string = "http://specmate.com/20181108/model/processes";
public url: string;
public className: string = "ProcessEnd";
public static className: string = "ProcessEnd";
diff --git a/web/src/app/model/ProcessNode.ts b/web/src/app/model/ProcessNode.ts
index 6c3b4135e..c7895d551 100644
--- a/web/src/app/model/ProcessNode.ts
+++ b/web/src/app/model/ProcessNode.ts
@@ -4,7 +4,7 @@
export class ProcessNode {
- ___nsuri: string = "http://specmate.com/20180925/model/processes";
+ ___nsuri: string = "http://specmate.com/20181108/model/processes";
public url: string;
public className: string = "ProcessNode";
public static className: string = "ProcessNode";
diff --git a/web/src/app/model/ProcessStart.ts b/web/src/app/model/ProcessStart.ts
index 247071fc3..e3b355a57 100644
--- a/web/src/app/model/ProcessStart.ts
+++ b/web/src/app/model/ProcessStart.ts
@@ -4,7 +4,7 @@
export class ProcessStart {
- ___nsuri: string = "http://specmate.com/20180925/model/processes";
+ ___nsuri: string = "http://specmate.com/20181108/model/processes";
public url: string;
public className: string = "ProcessStart";
public static className: string = "ProcessStart";
diff --git a/web/src/app/model/ProcessStep.ts b/web/src/app/model/ProcessStep.ts
index 1ce2d8e14..608043e06 100644
--- a/web/src/app/model/ProcessStep.ts
+++ b/web/src/app/model/ProcessStep.ts
@@ -4,7 +4,7 @@
export class ProcessStep {
- ___nsuri: string = "http://specmate.com/20180925/model/processes";
+ ___nsuri: string = "http://specmate.com/20181108/model/processes";
public url: string;
public className: string = "ProcessStep";
public static className: string = "ProcessStep";
diff --git a/web/src/app/model/Requirement.ts b/web/src/app/model/Requirement.ts
index 30dec9053..099573a34 100644
--- a/web/src/app/model/Requirement.ts
+++ b/web/src/app/model/Requirement.ts
@@ -4,7 +4,7 @@
export class Requirement {
- ___nsuri: string = "http://specmate.com/20180925/model/requirements";
+ ___nsuri: string = "http://specmate.com/20181108/model/requirements";
public url: string;
public className: string = "Requirement";
public static className: string = "Requirement";
diff --git a/web/src/app/model/Status.ts b/web/src/app/model/Status.ts
index 476a4db31..3cf4cf847 100644
--- a/web/src/app/model/Status.ts
+++ b/web/src/app/model/Status.ts
@@ -4,7 +4,7 @@
export class Status {
- ___nsuri: string = "http://specmate.com/20180925/model/administration";
+ ___nsuri: string = "http://specmate.com/20181108/model/administration";
public url: string;
public className: string = "Status";
public static className: string = "Status";
diff --git a/web/src/app/model/TestCase.ts b/web/src/app/model/TestCase.ts
index 315ae6fa0..d1bbace93 100644
--- a/web/src/app/model/TestCase.ts
+++ b/web/src/app/model/TestCase.ts
@@ -4,7 +4,7 @@
export class TestCase {
- ___nsuri: string = "http://specmate.com/20180925/model/testspecification";
+ ___nsuri: string = "http://specmate.com/20181108/model/testspecification";
public url: string;
public className: string = "TestCase";
public static className: string = "TestCase";
diff --git a/web/src/app/model/TestParameter.ts b/web/src/app/model/TestParameter.ts
index 4e1c0bad2..591b89442 100644
--- a/web/src/app/model/TestParameter.ts
+++ b/web/src/app/model/TestParameter.ts
@@ -4,7 +4,7 @@
export class TestParameter {
- ___nsuri: string = "http://specmate.com/20180925/model/testspecification";
+ ___nsuri: string = "http://specmate.com/20181108/model/testspecification";
public url: string;
public className: string = "TestParameter";
public static className: string = "TestParameter";
diff --git a/web/src/app/model/TestProcedure.ts b/web/src/app/model/TestProcedure.ts
index 475069258..eb6b945d7 100644
--- a/web/src/app/model/TestProcedure.ts
+++ b/web/src/app/model/TestProcedure.ts
@@ -4,7 +4,7 @@
export class TestProcedure {
- ___nsuri: string = "http://specmate.com/20180925/model/testspecification";
+ ___nsuri: string = "http://specmate.com/20181108/model/testspecification";
public url: string;
public className: string = "TestProcedure";
public static className: string = "TestProcedure";
diff --git a/web/src/app/model/TestSpecification.ts b/web/src/app/model/TestSpecification.ts
index 5f8a78003..eae83491d 100644
--- a/web/src/app/model/TestSpecification.ts
+++ b/web/src/app/model/TestSpecification.ts
@@ -4,7 +4,7 @@
export class TestSpecification {
- ___nsuri: string = "http://specmate.com/20180925/model/testspecification";
+ ___nsuri: string = "http://specmate.com/20181108/model/testspecification";
public url: string;
public className: string = "TestSpecification";
public static className: string = "TestSpecification";
diff --git a/web/src/app/model/TestSpecificationSkeleton.ts b/web/src/app/model/TestSpecificationSkeleton.ts
new file mode 100644
index 000000000..2a4f493e1
--- /dev/null
+++ b/web/src/app/model/TestSpecificationSkeleton.ts
@@ -0,0 +1,23 @@
+ import './support/gentypes';
+ import { Proxy } from './support/proxy';
+
+
+ export class TestSpecificationSkeleton {
+
+ ___nsuri: string = "http://specmate.com/20181108/model/testspecification";
+ public url: string;
+ public className: string = "TestSpecificationSkeleton";
+ public static className: string = "TestSpecificationSkeleton";
+
+ // Attributes
+ public name: EString;
+ public language: EString;
+ public code: EString;
+
+ // References
+
+ // Containment
+
+
+ }
+
diff --git a/web/src/app/model/TestStep.ts b/web/src/app/model/TestStep.ts
index 2e7bcfb0c..3045cf902 100644
--- a/web/src/app/model/TestStep.ts
+++ b/web/src/app/model/TestStep.ts
@@ -4,7 +4,7 @@
export class TestStep {
- ___nsuri: string = "http://specmate.com/20180925/model/testspecification";
+ ___nsuri: string = "http://specmate.com/20181108/model/testspecification";
public url: string;
public className: string = "TestStep";
public static className: string = "TestStep";
diff --git a/web/src/app/model/User.ts b/web/src/app/model/User.ts
index a860cedaa..0dd561fe9 100644
--- a/web/src/app/model/User.ts
+++ b/web/src/app/model/User.ts
@@ -4,13 +4,13 @@
export class User {
- ___nsuri: string = "http://specmate.com/20180925/model/user";
+ ___nsuri: string = "http://specmate.com/20181108/model/user";
public url: string;
public className: string = "User";
public static className: string = "User";
// Attributes
- public allowedUrls: EString;
+ public allowedUrls: EString[];
public userName: EString;
public passWord: EString;
public projectName: EString;
diff --git a/web/src/app/model/UserSession.ts b/web/src/app/model/UserSession.ts
index c36aa51dc..99b20fb42 100644
--- a/web/src/app/model/UserSession.ts
+++ b/web/src/app/model/UserSession.ts
@@ -4,7 +4,7 @@
export class UserSession {
- ___nsuri: string = "http://specmate.com/20180925/model/user";
+ ___nsuri: string = "http://specmate.com/20181108/model/user";
public url: string;
public className: string = "UserSession";
public static className: string = "UserSession";
@@ -18,7 +18,6 @@
public TargetSystem: AccessRights;
public libraryFolders: EString[];
-
// References
// Containment
diff --git a/web/src/app/model/meta/field-meta.ts b/web/src/app/model/meta/field-meta.ts
index e378686dd..04f135fa7 100644
--- a/web/src/app/model/meta/field-meta.ts
+++ b/web/src/app/model/meta/field-meta.ts
@@ -255,6 +255,15 @@ export class MetaInfo {
rows: '8',
position: '100'
} ];
+ public static TestSpecificationSkeleton: FieldMetaItem[] = [
+ {
+ name: "name",
+ shortDesc: 'Name',
+ longDesc: '',
+ required: true,
+ type: 'text',
+ position: '0'
+ } ];
public static TestParameter: FieldMetaItem[] = [
{
name: "name",
diff --git a/web/src/app/modules/actions/modules/common-controls/components/common-controls.component.html b/web/src/app/modules/actions/modules/common-controls/components/common-controls.component.html
index 7cccacffa..1a674dbae 100644
--- a/web/src/app/modules/actions/modules/common-controls/components/common-controls.component.html
+++ b/web/src/app/modules/actions/modules/common-controls/components/common-controls.component.html
@@ -1,7 +1,7 @@
{{'connection.lost' | translate}}
\ No newline at end of file
diff --git a/web/src/app/modules/actions/modules/common-controls/components/common-controls.component.ts b/web/src/app/modules/actions/modules/common-controls/components/common-controls.component.ts
index a6348f17e..09f3c58df 100644
--- a/web/src/app/modules/actions/modules/common-controls/components/common-controls.component.ts
+++ b/web/src/app/modules/actions/modules/common-controls/components/common-controls.component.ts
@@ -1,9 +1,10 @@
import { Component } from '@angular/core';
-import { SpecmateDataService } from '../../../../data/modules/data-service/services/specmate-data.service';
-import { NavigatorService } from '../../../../navigation/modules/navigator/services/navigator.service';
-import { ValidationService } from '../../../../forms/modules/validation/services/validation.service';
import { TranslateService } from '@ngx-translate/core';
import { ServerConnectionService } from '../../../../common/modules/connection/services/server-connection-service';
+import { UISafe } from '../../../../common/modules/ui/ui-safe-decorator';
+import { SpecmateDataService } from '../../../../data/modules/data-service/services/specmate-data.service';
+import { ValidationService } from '../../../../forms/modules/validation/services/validation.service';
+import { NavigatorService } from '../../../../navigation/modules/navigator/services/navigator.service';
@Component({
moduleId: module.id.toString(),
@@ -59,11 +60,16 @@ export class CommonControls {
}
public get isSaveEnabled(): boolean {
- return this.isEnabled && this.dataService.hasCommits && this.validator.currentValid;
+ return this.isEnabled && this.hasCommits && this.validator.currentValid;
}
public get isUndoEnabled(): boolean {
- return this.isEnabled && this.dataService.hasCommits;
+ return this.isEnabled && this.hasCommits;
+ }
+
+ @UISafe()
+ private get hasCommits(): boolean {
+ return this.dataService.hasCommits;
}
public get isBackEnabled(): boolean {
diff --git a/web/src/app/modules/actions/modules/export-testspecification-button/components/export-testspecification-button.component.css b/web/src/app/modules/actions/modules/export-testspecification-button/components/export-testspecification-button.component.css
new file mode 100644
index 000000000..4311c5e7a
--- /dev/null
+++ b/web/src/app/modules/actions/modules/export-testspecification-button/components/export-testspecification-button.component.css
@@ -0,0 +1,4 @@
+.btn.btn-link {
+ padding: 0px;
+ margin: 0px;
+}
diff --git a/web/src/app/modules/actions/modules/export-testspecification-button/components/export-testspecification-button.component.html b/web/src/app/modules/actions/modules/export-testspecification-button/components/export-testspecification-button.component.html
new file mode 100644
index 000000000..9d0500917
--- /dev/null
+++ b/web/src/app/modules/actions/modules/export-testspecification-button/components/export-testspecification-button.component.html
@@ -0,0 +1,2 @@
+
\ No newline at end of file
diff --git a/web/src/app/modules/actions/modules/export-testspecification-button/components/export-testspecification-button.component.ts b/web/src/app/modules/actions/modules/export-testspecification-button/components/export-testspecification-button.component.ts
new file mode 100644
index 000000000..793da542f
--- /dev/null
+++ b/web/src/app/modules/actions/modules/export-testspecification-button/components/export-testspecification-button.component.ts
@@ -0,0 +1,156 @@
+import { Component, OnInit, Input } from '@angular/core';
+import { TestSpecification } from '../../../../../model/TestSpecification';
+import { TestCase } from '../../../../../model/TestCase';
+import { TestParameter } from '../../../../../model/TestParameter';
+import { ParameterAssignment } from '../../../../../model/ParameterAssignment';
+import { Url } from '../../../../../util/url';
+import { Type } from '../../../../../util/type';
+import { IContainer } from '../../../../../model/IContainer';
+import { SpecmateDataService } from '../../../../data/modules/data-service/services/specmate-data.service';
+import { ValidationService } from '../../../../forms/modules/validation/services/validation.service';
+import { TranslateService } from '@ngx-translate/core';
+import { AuthenticationService } from '../../../../views/main/authentication/modules/auth/services/authentication.service';
+import { saveAs } from 'file-saver';
+
+@Component({
+ moduleId: module.id.toString(),
+ selector: 'export-testspecification-button',
+ templateUrl: 'export-testspecification-button.component.html',
+ styleUrls: ['export-testspecification-button.component.css']
+})
+
+export class ExportTestspecificationButton {
+
+ @Input()
+ public testSpecification: TestSpecification;
+
+ private contents: IContainer[];
+ private testCases: TestCase[];
+ private testParameters: TestParameter[];
+ private parameterAssignments: ParameterAssignment[];
+ private finalCsvString: string;
+
+ constructor(
+ private dataService: SpecmateDataService,
+ private validation: ValidationService) { }
+
+ // Export Function
+ public async exportTestSpecification(): Promise {
+ if (!this.enabled) {
+ return;
+ }
+ const contents = await this.dataService.readContents(this.testSpecification.url);
+ this.contents = contents;
+ await this.loadTestParameters();
+ await this.loadTestCases();
+ await Promise.all(this.loadParameterAssignments());
+ this.prepareExportString();
+ this.createDownloadFile();
+ }
+
+ private prepareExportString(): void {
+ this.pushHeaders();
+ this.pushRows();
+ }
+
+ private pushHeaders(): void {
+ let header = 'Test Cases,';
+ if (this.testParameters) {
+ for (let i = 0 ; i < this.testParameters.length ; i++) {
+ if (this.testParameters[i].type == 'INPUT') {
+ header += this.testParameters[i].type + ' - ' + this.testParameters[i].name + ', ';
+ }
+ }
+ for (let i = 0 ; i < this.testParameters.length ; i++) {
+ if (this.testParameters[i].type == 'OUTPUT') {
+ header += this.testParameters[i].type + ' - ' + this.testParameters[i].name + ', ';
+ }
+ }
+ }
+ header += '\n';
+ this.finalCsvString = header;
+ }
+
+ private pushRows(): void {
+ let testCasesString = '';
+ if (this.testCases) {
+ for (let i = 0 ; i < this.testCases.length ; i++) {
+ testCasesString += this.testCases[i].name + ', ';
+ for (let j = 0 ; j < this.testParameters.length; j++) {
+ let assignmentsList = this.getAssignments(this.testParameters[j]);
+ for (let k = 0 ; k < assignmentsList.length; k++) {
+ if (Url.parent(assignmentsList[k].url) == this.testCases[i].url) {
+ testCasesString += assignmentsList[k].condition + ', ';
+ break;
+ }
+ }
+ }
+ testCasesString += '\n';
+ }
+ }
+ this.finalCsvString += testCasesString;
+ }
+ private getAssignments(testParameter: TestParameter): ParameterAssignment[] {
+ let assignmentsList: ParameterAssignment[];
+ assignmentsList = [];
+ for (let i = 0 ; i < this.parameterAssignments.length; i++) {
+ if (this.parameterAssignments[i].parameter.url == testParameter.url) {
+ assignmentsList.push(this.parameterAssignments[i]);
+ }
+ }
+ return assignmentsList;
+ }
+
+ private loadTestCases(): void {
+ this.testCases = this.contents.filter((element: IContainer) => Type.is(element, TestCase))
+ .map((element: IContainer) => element as TestCase);
+ }
+
+ private loadTestParameters(): Promise {
+ let loadTestParametersTask: Promise = Promise.resolve();
+ loadTestParametersTask = loadTestParametersTask.then(() => {
+ this.testParameters = this.contents.filter((element: IContainer) => Type.is(element, TestParameter))
+ .map((element: IContainer) => element as TestParameter);
+ });
+ return loadTestParametersTask;
+ }
+
+ private loadParameterAssignments(): Promise[] {
+ let testCases: TestCase[] = this.testCases;
+ this.parameterAssignments = [];
+ let promiseArray: Promise[];
+ promiseArray = [];
+
+ let loadParameterAssignmentsTask: Promise = Promise.resolve();
+ for (let i = 0; i < testCases.length; i++) {
+ let currentTestCase: TestCase = testCases[i];
+ loadParameterAssignmentsTask = loadParameterAssignmentsTask.then(() => {
+ return this.dataService.readContents(currentTestCase.url)
+ .then((contents: IContainer[]) =>
+ contents.forEach((element: IContainer) => {
+ if (element.className == 'ParameterAssignment') {
+ this.parameterAssignments.push(element as ParameterAssignment);
+ }
+ }));
+ });
+ promiseArray.push(loadParameterAssignmentsTask);
+ }
+ return promiseArray;
+ }
+
+ private createDownloadFile(): void {
+ const blob = new Blob(['\ufeff', this.finalCsvString], { type: 'text/csv;charset=utf-8;' });
+ saveAs(blob, this.testSpecification.name + '.csv');
+ }
+
+ public get enabled(): boolean {
+ return this.isValid();
+ }
+
+ private isValid(): boolean {
+ if (this.testSpecification === undefined) {
+ return false;
+ }
+ return this.validation.isValid(this.testSpecification, this.contents);
+ }
+}
diff --git a/web/src/app/modules/actions/modules/export-testspecification-button/export-testspecification-button.module.ts b/web/src/app/modules/actions/modules/export-testspecification-button/export-testspecification-button.module.ts
new file mode 100644
index 000000000..75f2789ae
--- /dev/null
+++ b/web/src/app/modules/actions/modules/export-testspecification-button/export-testspecification-button.module.ts
@@ -0,0 +1,28 @@
+import { NgModule } from '@angular/core';
+import { ExportTestspecificationButton } from './components/export-testspecification-button.component';
+import { BrowserModule } from '@angular/platform-browser';
+import { TranslateModule } from '@ngx-translate/core';
+
+@NgModule({
+ imports: [
+ // MODULE IMPORTS
+ BrowserModule,
+ TranslateModule
+ ],
+ declarations: [
+ // COMPONENTS IN THIS MODULE
+ ExportTestspecificationButton
+ ],
+ exports: [
+ // THE COMPONENTS VISIBLE TO THE OUTSIDE
+ ExportTestspecificationButton
+ ],
+ providers: [
+ // SERVICES
+ ],
+ bootstrap: [
+ // COMPONENTS THAT ARE BOOTSTRAPPED HERE
+ ]
+})
+
+export class ExportTestspecificationButtonModule { }
diff --git a/web/src/app/modules/actions/modules/get-test-specification-skeleton-button/components/get-test-specification-skeleton-button.component.css b/web/src/app/modules/actions/modules/get-test-specification-skeleton-button/components/get-test-specification-skeleton-button.component.css
new file mode 100644
index 000000000..4311c5e7a
--- /dev/null
+++ b/web/src/app/modules/actions/modules/get-test-specification-skeleton-button/components/get-test-specification-skeleton-button.component.css
@@ -0,0 +1,4 @@
+.btn.btn-link {
+ padding: 0px;
+ margin: 0px;
+}
diff --git a/web/src/app/modules/actions/modules/get-test-specification-skeleton-button/components/get-test-specification-skeleton-button.component.html b/web/src/app/modules/actions/modules/get-test-specification-skeleton-button/components/get-test-specification-skeleton-button.component.html
new file mode 100644
index 000000000..d36315e8f
--- /dev/null
+++ b/web/src/app/modules/actions/modules/get-test-specification-skeleton-button/components/get-test-specification-skeleton-button.component.html
@@ -0,0 +1 @@
+
diff --git a/web/src/app/modules/actions/modules/get-test-specification-skeleton-button/components/get-test-specification-skeleton-button.component.ts b/web/src/app/modules/actions/modules/get-test-specification-skeleton-button/components/get-test-specification-skeleton-button.component.ts
new file mode 100644
index 000000000..1c6ac5838
--- /dev/null
+++ b/web/src/app/modules/actions/modules/get-test-specification-skeleton-button/components/get-test-specification-skeleton-button.component.ts
@@ -0,0 +1,64 @@
+import { Component, Input } from '@angular/core';
+import { SpecmateDataService } from '../../../../data/modules/data-service/services/specmate-data.service';
+import { TestSpecification } from '../../../../../model/TestSpecification';
+import { TranslateService } from '@ngx-translate/core';
+import { saveAs } from 'file-saver';
+import { LowerCasePipe } from '@angular/common';
+import { TestSpecificationSkeleton } from '../../../../../model/TestSpecificationSkeleton';
+
+@Component({
+ moduleId: module.id.toString(),
+ selector: 'get-test-specification-skeleton-button',
+ templateUrl: 'get-test-specification-skeleton-button.component.html',
+ styleUrls: ['get-test-specification-skeleton-button.component.css']
+})
+
+export class GetTestSpecificationSkeletonButton {
+
+ private _testspecification: TestSpecification;
+
+ private _lang: string;
+
+ @Input()
+ public set testspecification(testspecification: TestSpecification) {
+ if (!testspecification) {
+ return;
+ }
+ this._testspecification = testspecification;
+ }
+
+ @Input()
+ public set language(lang: string) {
+ this._lang = lang;
+ }
+
+ constructor(private dataService: SpecmateDataService,
+ private translate: TranslateService) { }
+
+ public async getskeleton(): Promise {
+ if (!this.enabled) {
+ return;
+ }
+
+ const data: TestSpecificationSkeleton = await this.dataService.performQuery(this._testspecification.url, 'generateTestSkeleton',
+ { language: new LowerCasePipe().transform(this._lang)});
+
+ if (data === undefined) {
+ throw new Error('Could not load test specification skeleton for ' + this._lang);
+ }
+
+ saveAs(new Blob([data.code], {type: 'text/plain;charset=utf-8'}), data.name);
+ }
+
+ public get language(): string {
+ return this._lang;
+ }
+
+ public get enabled(): boolean {
+ if (this._testspecification === undefined) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/web/src/app/modules/actions/modules/get-test-specification-skeleton-button/get-test-specification-skeleton-button.module.ts b/web/src/app/modules/actions/modules/get-test-specification-skeleton-button/get-test-specification-skeleton-button.module.ts
new file mode 100644
index 000000000..00bdb6cc0
--- /dev/null
+++ b/web/src/app/modules/actions/modules/get-test-specification-skeleton-button/get-test-specification-skeleton-button.module.ts
@@ -0,0 +1,28 @@
+import { NgModule } from '@angular/core';
+import { GetTestSpecificationSkeletonButton } from './components/get-test-specification-skeleton-button.component';
+import { BrowserModule } from '@angular/platform-browser';
+import { TranslateModule } from '@ngx-translate/core';
+
+@NgModule({
+ imports: [
+ // MODULE IMPORTS
+ BrowserModule,
+ TranslateModule
+ ],
+ declarations: [
+ // COMPONENTS IN THIS MODULE
+ GetTestSpecificationSkeletonButton
+ ],
+ exports: [
+ // THE COMPONENTS VISIBLE TO THE OUTSIDE
+ GetTestSpecificationSkeletonButton
+ ],
+ providers: [
+ // SERVICES
+ ],
+ bootstrap: [
+ // COMPONENTS THAT ARE BOOTSTRAPPED HERE
+ ]
+})
+
+export class GetTestSpecificationSkeletonButtonModule { }
diff --git a/web/src/app/modules/actions/modules/test-specification-generator-button/components/test-specification-generator-button.component.ts b/web/src/app/modules/actions/modules/test-specification-generator-button/components/test-specification-generator-button.component.ts
index 20343d87c..1c5f472c5 100644
--- a/web/src/app/modules/actions/modules/test-specification-generator-button/components/test-specification-generator-button.component.ts
+++ b/web/src/app/modules/actions/modules/test-specification-generator-button/components/test-specification-generator-button.component.ts
@@ -9,14 +9,6 @@ import { TestSpecification } from '../../../../../model/TestSpecification';
import { Id } from '../../../../../util/id';
import { Url } from '../../../../../util/url';
import { Config } from '../../../../../config/config';
-import { Type } from '../../../../../util/type';
-import { CEGNode } from '../../../../../model/CEGNode';
-import { ProcessStart } from '../../../../../model/ProcessStart';
-import { ProcessEnd } from '../../../../../model/ProcessEnd';
-import { IModelNode } from '../../../../../model/IModelNode';
-import { ProcessStep } from '../../../../../model/ProcessStep';
-import { ProcessConnection } from '../../../../../model/ProcessConnection';
-import { ProcessDecision } from '../../../../../model/ProcessDecision';
import { ValidationService } from '../../../../forms/modules/validation/services/validation.service';
import { ValidationResult } from '../../../../../validation/validation-result';
import { TranslateService } from '@ngx-translate/core';
diff --git a/web/src/app/modules/common/modules/ui/ui-safe-decorator.ts b/web/src/app/modules/common/modules/ui/ui-safe-decorator.ts
new file mode 100644
index 000000000..1f1a9738d
--- /dev/null
+++ b/web/src/app/modules/common/modules/ui/ui-safe-decorator.ts
@@ -0,0 +1,33 @@
+/**
+ * Adding @UISafe() to a getter delays changes to the underlying value until the next update cycle.
+ * This prevents ExpressionChangedAfterItHasBeenCheckedErrors
+ */
+export function UISafe(): MethodDecorator {
+ return function(target: any, propertyKey: string| symbol, descriptor: PropertyDescriptor) {
+ let originalMethod = descriptor.get;
+ if (!originalMethod) {
+ throw new Error('@UISafe can only decorate getters.');
+ }
+ if (!descriptor.configurable) {
+ throw new Error('@UISafe target must be configurable.');
+ }
+ let oldValue: any = undefined;
+ let firstCall = true;
+ descriptor.get = function() {
+ let context = this;
+ let args = arguments;
+ let newValue = originalMethod.apply(context, args);
+ if (firstCall) {
+ oldValue = newValue;
+ firstCall = false;
+ }
+ if (oldValue !== newValue) {
+ setTimeout(() => {
+ oldValue = newValue;
+ });
+ }
+ return oldValue;
+ };
+ return descriptor;
+ };
+}
diff --git a/web/src/app/modules/data/modules/data-service/services/specmate-data.service.ts b/web/src/app/modules/data/modules/data-service/services/specmate-data.service.ts
index ad679f9c5..96558ee36 100644
--- a/web/src/app/modules/data/modules/data-service/services/specmate-data.service.ts
+++ b/web/src/app/modules/data/modules/data-service/services/specmate-data.service.ts
@@ -43,6 +43,7 @@ export class SpecmateDataService {
}
public stateChanged: EventEmitter;
+ public committed: EventEmitter;
private cache: DataCache = new DataCache();
private serviceInterface: ServiceInterface;
@@ -57,6 +58,7 @@ export class SpecmateDataService {
this.serviceInterface = new ServiceInterface(http);
this.scheduler = new Scheduler(this, this.logger, this.translate);
this.stateChanged = new EventEmitter();
+ this.committed = new EventEmitter();
this.auth.authChanged.subscribe(() => {
if (!this.auth.isAuthenticated) {
@@ -190,6 +192,7 @@ export class SpecmateDataService {
this.scheduler.resolveBatchOperation(batchOperation);
this.scheduler.clearCommits();
this.busy = false;
+ this.committed.emit();
}
public undo(): void {
diff --git a/web/src/app/modules/forms/modules/generic-form/components/generic-form.component.ts b/web/src/app/modules/forms/modules/generic-form/components/generic-form.component.ts
index 9a0a1dae1..f7515827b 100644
--- a/web/src/app/modules/forms/modules/generic-form/components/generic-form.component.ts
+++ b/web/src/app/modules/forms/modules/generic-form/components/generic-form.component.ts
@@ -142,7 +142,8 @@ export class GenericForm {
updateValue = converter.convertFromControlToModel(updateValue);
}
// We do not need to clone here (hopefully), because only simple values can be passed via forms.
- if (this.element[fieldName] + '' !== updateValue + '') {
+ const originalValue = this.element[fieldName] || '';
+ if (originalValue !== updateValue + '') {
this.element[fieldName] = updateValue;
changed = true;
}
diff --git a/web/src/app/modules/specmate/components/specmate.component.html b/web/src/app/modules/specmate/components/specmate.component.html
index d38a35479..4a9447cc6 100644
--- a/web/src/app/modules/specmate/components/specmate.component.html
+++ b/web/src/app/modules/specmate/components/specmate.component.html
@@ -1,5 +1,8 @@
-
+
+
+
+
@@ -16,7 +19,7 @@
-
+
diff --git a/web/src/app/modules/views/main/authentication/modules/login/components/login.component.html b/web/src/app/modules/views/main/authentication/modules/login/components/login.component.html
index 44e9fec42..b6296fa15 100644
--- a/web/src/app/modules/views/main/authentication/modules/login/components/login.component.html
+++ b/web/src/app/modules/views/main/authentication/modules/login/components/login.component.html
@@ -62,4 +62,4 @@
{{'login' | translate}}
-
\ No newline at end of file
+
diff --git a/web/src/app/modules/views/main/editors/modules/contents-container/base/contents-container-base.ts b/web/src/app/modules/views/main/editors/modules/contents-container/base/contents-container-base.ts
index e88f249b9..6817d572a 100644
--- a/web/src/app/modules/views/main/editors/modules/contents-container/base/contents-container-base.ts
+++ b/web/src/app/modules/views/main/editors/modules/contents-container/base/contents-container-base.ts
@@ -4,7 +4,10 @@ import { TranslateService } from '@ngx-translate/core';
import { IContainer } from '../../../../../../../model/IContainer';
import { ConfirmationModal } from '../../../../../../notification/modules/modals/services/confirmation-modal.service';
import { Id } from '../../../../../../../util/id';
-import { OnInit, Input } from '@angular/core';
+import { OnInit, Input, Type } from '@angular/core';
+import { Objects } from '../../../../../../../util/objects';
+import { ClipboardService } from '../../tool-pallette/services/clipboard-service';
+import { GraphTransformer } from '../../tool-pallette/util/graphTransformer';
export abstract class ContentContainerBase implements OnInit {
@@ -27,7 +30,8 @@ export abstract class ContentContainerBase implements OnIn
protected dataService: SpecmateDataService,
protected navigator: NavigatorService,
protected translate: TranslateService,
- protected modal: ConfirmationModal) {
+ protected modal: ConfirmationModal,
+ private clipboardService: ClipboardService) {
}
public async ngOnInit(): Promise {
@@ -64,4 +68,50 @@ export abstract class ContentContainerBase implements OnIn
const contents = await this.dataService.readContents(this.parent.url, false);
this.contents = contents.filter(this.condition);
}
+
+ private get clipboardModel(): IContainer {
+ if (this.canPaste) {
+ return this.clipboardService.clipboard[0] as IContainer;
+ }
+ return undefined;
+ }
+
+ public get clipboardModelName(): string {
+ if (this.canPaste) {
+ return this.clipboardModel.name;
+ }
+ return this.translate.instant('pasteAsName');
+ }
+
+ public async copy(model: IContainer): Promise {
+ const copy = Objects.clone(model) as IContainer;
+ copy.description = model.description;
+ copy.id = Id.uuid;
+ copy.name = this.translate.instant('copyOf') + ' ' + model.name;
+ this.clipboardService.clipboard = [copy];
+ }
+
+ public async paste(name: string): Promise {
+ if (!this.canPaste) {
+ return;
+ }
+ const model = this.clipboardModel;
+ const pasteName = name || this.clipboardModelName;
+ const copy = await this.createElement(pasteName);
+ copy.description = model.description;
+
+ const contents = await this.dataService.readContents(model.url, true);
+ const transformer = new GraphTransformer(this.dataService, undefined, copy);
+
+ const compoundId = Id.uuid;
+ await this.dataService.updateElement(copy, true, compoundId);
+ await transformer.cloneSubgraph(contents, compoundId, true);
+ await this.dataService.commit(this.translate.instant('paste'));
+ await this.readContents();
+ this.clipboardService.clear();
+ }
+
+ public get canPaste(): boolean {
+ return this.clipboardService.clipboard.length === 1 && this.condition(this.clipboardService.clipboard[0]);
+ }
}
diff --git a/web/src/app/modules/views/main/editors/modules/contents-container/base/testspecification-generatable-content-container-base.ts b/web/src/app/modules/views/main/editors/modules/contents-container/base/testspecification-generatable-content-container-base.ts
new file mode 100644
index 000000000..beb63dcbf
--- /dev/null
+++ b/web/src/app/modules/views/main/editors/modules/contents-container/base/testspecification-generatable-content-container-base.ts
@@ -0,0 +1,26 @@
+import { ContentContainerBase } from './contents-container-base';
+import { IContainer } from '../../../../../../../model/IContainer';
+import { SpecmateDataService } from '../../../../../../data/modules/data-service/services/specmate-data.service';
+import { NavigatorService } from '../../../../../../navigation/modules/navigator/services/navigator.service';
+import { TranslateService } from '@ngx-translate/core';
+import { ConfirmationModal } from '../../../../../../notification/modules/modals/services/confirmation-modal.service';
+import { ClipboardService } from '../../tool-pallette/services/clipboard-service';
+import { AdditionalInformationService } from '../../../../../side/modules/links-actions/services/additional-information.service';
+
+export abstract class TestSpecificationContentContainerBase extends ContentContainerBase {
+
+ constructor(
+ dataService: SpecmateDataService,
+ navigator: NavigatorService,
+ translate: TranslateService,
+ modal: ConfirmationModal,
+ clipboardService: ClipboardService,
+ protected additionalInformationService: AdditionalInformationService) {
+ super(dataService, navigator, translate, modal, clipboardService);
+ }
+
+ public get canGenerateTestSpecification(): boolean {
+ return this.additionalInformationService.canGenerateTestSpecifications;
+ }
+
+}
diff --git a/web/src/app/modules/views/main/editors/modules/contents-container/components/ceg-model-container.component.html b/web/src/app/modules/views/main/editors/modules/contents-container/components/ceg-model-container.component.html
index f14fc7325..0fad520b6 100644
--- a/web/src/app/modules/views/main/editors/modules/contents-container/components/ceg-model-container.component.html
+++ b/web/src/app/modules/views/main/editors/modules/contents-container/components/ceg-model-container.component.html
@@ -20,13 +20,18 @@
{{element.description | truncate: 60}}
-
- |
@@ -41,13 +46,24 @@
+ class="btn btn-sm btn-outline-primary" [disabled]="newNameInput.value === undefined || newNameInput.value.length === 0"
+ [class.disabled]="newNameInput.value === undefined || newNameInput.value.length === 0">
{{'createModel' | translate}}
-
+
\ No newline at end of file
diff --git a/web/src/app/modules/views/main/editors/modules/contents-container/components/ceg-model-container.component.ts b/web/src/app/modules/views/main/editors/modules/contents-container/components/ceg-model-container.component.ts
index f6db7cb04..be2051d06 100644
--- a/web/src/app/modules/views/main/editors/modules/contents-container/components/ceg-model-container.component.ts
+++ b/web/src/app/modules/views/main/editors/modules/contents-container/components/ceg-model-container.component.ts
@@ -10,6 +10,9 @@ import { NavigatorService } from '../../../../../../navigation/modules/navigator
import { TranslateService } from '@ngx-translate/core';
import { ConfirmationModal } from '../../../../../../notification/modules/modals/services/confirmation-modal.service';
import { Id } from '../../../../../../../util/id';
+import { ClipboardService } from '../../tool-pallette/services/clipboard-service';
+import { TestSpecificationContentContainerBase } from '../base/testspecification-generatable-content-container-base';
+import { AdditionalInformationService } from '../../../../../side/modules/links-actions/services/additional-information.service';
@Component({
moduleId: module.id.toString(),
@@ -18,10 +21,15 @@ import { Id } from '../../../../../../../util/id';
styleUrls: ['ceg-model-container.component.css']
})
-export class CEGModelContainer extends ContentContainerBase {
+export class CEGModelContainer extends TestSpecificationContentContainerBase {
- constructor(dataService: SpecmateDataService, navigator: NavigatorService, translate: TranslateService, modal: ConfirmationModal) {
- super(dataService, navigator, translate, modal);
+ constructor(dataService: SpecmateDataService,
+ navigator: NavigatorService,
+ translate: TranslateService,
+ modal: ConfirmationModal,
+ clipboardService: ClipboardService,
+ additionalInformationService: AdditionalInformationService) {
+ super(dataService, navigator, translate, modal, clipboardService, additionalInformationService);
}
protected condition = (element: IContainer) => Type.is(element, CEGModel);
diff --git a/web/src/app/modules/views/main/editors/modules/contents-container/components/folder-container.component.ts b/web/src/app/modules/views/main/editors/modules/contents-container/components/folder-container.component.ts
index 29935adf4..3ae2bba06 100644
--- a/web/src/app/modules/views/main/editors/modules/contents-container/components/folder-container.component.ts
+++ b/web/src/app/modules/views/main/editors/modules/contents-container/components/folder-container.component.ts
@@ -9,6 +9,7 @@ import { ConfirmationModal } from '../../../../../../notification/modules/modals
import { Id } from '../../../../../../../util/id';
import { Folder } from '../../../../../../../model/Folder';
import { FolderFactory } from '../../../../../../../factory/folder-factory';
+import { ClipboardService } from '../../tool-pallette/services/clipboard-service';
@Component({
moduleId: module.id.toString(),
@@ -19,8 +20,12 @@ import { FolderFactory } from '../../../../../../../factory/folder-factory';
export class FolderContainer extends ContentContainerBase {
- constructor(dataService: SpecmateDataService, navigator: NavigatorService, translate: TranslateService, modal: ConfirmationModal) {
- super(dataService, navigator, translate, modal);
+ constructor(dataService: SpecmateDataService,
+ navigator: NavigatorService,
+ translate: TranslateService,
+ modal: ConfirmationModal,
+ clipboardService: ClipboardService) {
+ super(dataService, navigator, translate, modal, clipboardService);
}
protected condition = (element: IContainer) => Type.is(element, Folder);
diff --git a/web/src/app/modules/views/main/editors/modules/contents-container/components/process-model-container.component.html b/web/src/app/modules/views/main/editors/modules/contents-container/components/process-model-container.component.html
index b29d693a9..337c7b63f 100644
--- a/web/src/app/modules/views/main/editors/modules/contents-container/components/process-model-container.component.html
+++ b/web/src/app/modules/views/main/editors/modules/contents-container/components/process-model-container.component.html
@@ -20,13 +20,18 @@
{{element.description | truncate: 60}}
-
-
-
+
+
+ {{'copy' | translate}}
-
+
+ {{'duplicate' | translate}}
+
+
|
@@ -41,13 +46,25 @@
+ class="btn btn-sm btn-outline-primary" [disabled]="newNameInput.value === undefined || newNameInput.value.length === 0"
+ [class.disabled]="newNameInput.value === undefined || newNameInput.value.length === 0">
{{'createProcess' | translate}}
-
+
\ No newline at end of file
diff --git a/web/src/app/modules/views/main/editors/modules/contents-container/components/process-model-container.component.ts b/web/src/app/modules/views/main/editors/modules/contents-container/components/process-model-container.component.ts
index abd631553..be4f3606e 100644
--- a/web/src/app/modules/views/main/editors/modules/contents-container/components/process-model-container.component.ts
+++ b/web/src/app/modules/views/main/editors/modules/contents-container/components/process-model-container.component.ts
@@ -10,6 +10,9 @@ import { NavigatorService } from '../../../../../../navigation/modules/navigator
import { TranslateService } from '@ngx-translate/core';
import { ConfirmationModal } from '../../../../../../notification/modules/modals/services/confirmation-modal.service';
import { Id } from '../../../../../../../util/id';
+import { ClipboardService } from '../../tool-pallette/services/clipboard-service';
+import { TestSpecificationContentContainerBase } from '../base/testspecification-generatable-content-container-base';
+import { AdditionalInformationService } from '../../../../../side/modules/links-actions/services/additional-information.service';
@Component({
moduleId: module.id.toString(),
@@ -18,10 +21,15 @@ import { Id } from '../../../../../../../util/id';
styleUrls: ['process-model-container.component.css']
})
-export class ProcessModelContainer extends ContentContainerBase {
+export class ProcessModelContainer extends TestSpecificationContentContainerBase {
- constructor(dataService: SpecmateDataService, navigator: NavigatorService, translate: TranslateService, modal: ConfirmationModal) {
- super(dataService, navigator, translate, modal);
+ constructor(dataService: SpecmateDataService,
+ navigator: NavigatorService,
+ translate: TranslateService,
+ modal: ConfirmationModal,
+ additionalInformationService: AdditionalInformationService,
+ clipboardService: ClipboardService) {
+ super(dataService, navigator, translate, modal, clipboardService, additionalInformationService);
}
protected condition = (element: IContainer) => Type.is(element, Process);
diff --git a/web/src/app/modules/views/main/editors/modules/contents-container/components/related-requirements-container.component.ts b/web/src/app/modules/views/main/editors/modules/contents-container/components/related-requirements-container.component.ts
index 0e00f9a2b..e7a5f5152 100644
--- a/web/src/app/modules/views/main/editors/modules/contents-container/components/related-requirements-container.component.ts
+++ b/web/src/app/modules/views/main/editors/modules/contents-container/components/related-requirements-container.component.ts
@@ -1,17 +1,13 @@
import { Component, Input } from '@angular/core';
import { ContentContainerBase } from '../base/contents-container-base';
import { IContainer } from '../../../../../../../model/IContainer';
-import { ModelFactoryBase } from '../../../../../../../factory/model-factory-base';
-import { Type } from '../../../../../../../util/type';
-import { Process } from '../../../../../../../model/Process';
-import { ProcessFactory } from '../../../../../../../factory/process-factory';
import { SpecmateDataService } from '../../../../../../data/modules/data-service/services/specmate-data.service';
import { NavigatorService } from '../../../../../../navigation/modules/navigator/services/navigator.service';
import { TranslateService } from '@ngx-translate/core';
import { ConfirmationModal } from '../../../../../../notification/modules/modals/services/confirmation-modal.service';
-import { Id } from '../../../../../../../util/id';
import { Requirement } from '../../../../../../../model/Requirement';
import { Sort } from '../../../../../../../util/sort';
+import { ClipboardService } from '../../tool-pallette/services/clipboard-service';
@Component({
moduleId: module.id.toString(),
@@ -22,8 +18,12 @@ import { Sort } from '../../../../../../../util/sort';
export class RelatedRequirementsContainer extends ContentContainerBase {
- constructor(dataService: SpecmateDataService, navigator: NavigatorService, translate: TranslateService, modal: ConfirmationModal) {
- super(dataService, navigator, translate, modal);
+ constructor(dataService: SpecmateDataService,
+ navigator: NavigatorService,
+ translate: TranslateService,
+ modal: ConfirmationModal,
+ clipboardService: ClipboardService) {
+ super(dataService, navigator, translate, modal, clipboardService);
}
protected condition = (element: IContainer) => true;
diff --git a/web/src/app/modules/views/main/editors/modules/contents-container/components/test-specification-container.component.html b/web/src/app/modules/views/main/editors/modules/contents-container/components/test-specification-container.component.html
index 09a6653cb..2c8d09666 100644
--- a/web/src/app/modules/views/main/editors/modules/contents-container/components/test-specification-container.component.html
+++ b/web/src/app/modules/views/main/editors/modules/contents-container/components/test-specification-container.component.html
@@ -22,7 +22,7 @@
-
+ {{'duplicate' | translate}}
diff --git a/web/src/app/modules/views/main/editors/modules/contents-container/components/test-specification-container.component.ts b/web/src/app/modules/views/main/editors/modules/contents-container/components/test-specification-container.component.ts
index cf9f51c40..6a5caba9d 100644
--- a/web/src/app/modules/views/main/editors/modules/contents-container/components/test-specification-container.component.ts
+++ b/web/src/app/modules/views/main/editors/modules/contents-container/components/test-specification-container.component.ts
@@ -1,10 +1,7 @@
import { Component, Input } from '@angular/core';
import { ContentContainerBase } from '../base/contents-container-base';
import { IContainer } from '../../../../../../../model/IContainer';
-import { ModelFactoryBase } from '../../../../../../../factory/model-factory-base';
-import { CEGModelFactory } from '../../../../../../../factory/ceg-model-factory';
import { Type } from '../../../../../../../util/type';
-import { Process } from '../../../../../../../model/Process';
import { TestSpecification } from '../../../../../../../model/TestSpecification';
import { TestSpecificationFactory } from '../../../../../../../factory/test-specification-factory';
import { ElementFactoryBase } from '../../../../../../../factory/element-factory-base';
@@ -13,9 +10,9 @@ import { SpecmateDataService } from '../../../../../../data/modules/data-service
import { NavigatorService } from '../../../../../../navigation/modules/navigator/services/navigator.service';
import { TranslateService } from '@ngx-translate/core';
import { ConfirmationModal } from '../../../../../../notification/modules/modals/services/confirmation-modal.service';
-import { UUID } from 'angular2-uuid';
import { Id } from '../../../../../../../util/id';
import { TestCase } from '../../../../../../../model/TestCase';
+import { ClipboardService } from '../../tool-pallette/services/clipboard-service';
@Component({
moduleId: module.id.toString(),
@@ -26,8 +23,12 @@ import { TestCase } from '../../../../../../../model/TestCase';
export class TestSpecificationContainer extends ContentContainerBase {
- constructor(dataService: SpecmateDataService, navigator: NavigatorService, translate: TranslateService, modal: ConfirmationModal) {
- super(dataService, navigator, translate, modal);
+ constructor(dataService: SpecmateDataService,
+ navigator: NavigatorService,
+ translate: TranslateService,
+ modal: ConfirmationModal,
+ clipboardService: ClipboardService) {
+ super(dataService, navigator, translate, modal, clipboardService);
}
protected condition = (element: IContainer) => Type.is(element, TestSpecification);
diff --git a/web/src/app/modules/views/main/editors/modules/graphical-editor/components/common/truncated-text.component.svg b/web/src/app/modules/views/main/editors/modules/graphical-editor/components/common/truncated-text.component.svg
index ce27c3449..3dee2652b 100644
--- a/web/src/app/modules/views/main/editors/modules/graphical-editor/components/common/truncated-text.component.svg
+++ b/web/src/app/modules/views/main/editors/modules/graphical-editor/components/common/truncated-text.component.svg
@@ -1 +1 @@
-{{adjustedText}}
+{{line}}
diff --git a/web/src/app/modules/views/main/editors/modules/graphical-editor/components/common/truncated-text.component.ts b/web/src/app/modules/views/main/editors/modules/graphical-editor/components/common/truncated-text.component.ts
index b04bfcbd0..a96462419 100644
--- a/web/src/app/modules/views/main/editors/modules/graphical-editor/components/common/truncated-text.component.ts
+++ b/web/src/app/modules/views/main/editors/modules/graphical-editor/components/common/truncated-text.component.ts
@@ -26,27 +26,57 @@ export class TruncatedText {
@Input()
public width: number;
+ @Input()
+ public height = 30;
+
@Input()
public centered = true;
- private _adjustedText: string;
- public get adjustedText(): string {
- if (this.stringWidth(this.text) <= this.width) {
- return this.text;
- }
- let ellipsisWidth: number = this.stringWidth(this.ellipsis);
- for (let i = this.text.length - 1; i >= 0; i--) {
- let truncatedText: string = this.text.substring(0, i);
- let widthWithEllipsis: number = this.stringWidth(truncatedText) + ellipsisWidth;
- if (widthWithEllipsis <= this.width) {
- this._adjustedText = truncatedText + this.ellipsis;
- break;
+ public lineHeight = 15;
+
+ public get lines(): string[] {
+ const width = this.width / 10;
+ const numLines = this.height / this.lineHeight;
+ const lineBags: string[][] = [];
+ const wordSep = ' ';
+ const words = this.text.split(wordSep).map(word => this.truncate(word, width, this.ellipsis));
+ let wordIndex = 0;
+ let lineIndex = 0;
+ while (wordIndex < words.length && lineIndex < numLines) {
+ if (lineBags[lineIndex] === undefined) {
+ lineBags[lineIndex] = [];
+ }
+
+ const currentWord = words[wordIndex];
+ const contentLength = lineBags[lineIndex].join(wordSep).length;
+ const contentLengthWithWord = contentLength + (contentLength === 0 ? 0 : wordSep.length) + currentWord.length;
+
+ if (contentLengthWithWord <= width) {
+ lineBags[lineIndex].push(currentWord);
+ wordIndex++;
+ } else {
+ lineIndex++;
}
}
- return this._adjustedText;
+
+ const lines = lineBags.map(words => words.join(wordSep));
+ if (wordIndex < words.length) {
+ lines[lines.length - 1] = this.truncate(lines[lines.length - 1], width, this.ellipsis, wordIndex < words.length);
+ }
+ return lines;
}
- private stringWidth(str: string): number {
- return str.length * 10;
+ private truncate(word: string, width: number, ellipsis: string, force = false): string {
+ if (force) {
+ if (word.length + ellipsis.length <= width) {
+ return word + ellipsis;
+ } else {
+ return word.substring(0, width - ellipsis.length) + ellipsis;
+ }
+ }
+ if (word.length > width) {
+ return word.substring(0, width - ellipsis.length) + ellipsis;
+ }
+ return word;
}
}
diff --git a/web/src/app/modules/views/main/editors/modules/graphical-editor/components/graphical-editor.component.html b/web/src/app/modules/views/main/editors/modules/graphical-editor/components/graphical-editor.component.html
index bdce96867..27c4d1b83 100644
--- a/web/src/app/modules/views/main/editors/modules/graphical-editor/components/graphical-editor.component.html
+++ b/web/src/app/modules/views/main/editors/modules/graphical-editor/components/graphical-editor.component.html
@@ -16,7 +16,7 @@ |