Browse Source

DB: TestCaseLog added

aguryev 4 years ago
parent
commit
c134ad2ea0
51 changed files with 134 additions and 6786 deletions
  1. 6 10
      .gitignore
  2. BIN
      SimpleTheInternet.xlsx
  3. 53 1
      baangt/base/DataBaseORM.py
  4. 63 3
      baangt/base/ExportResults/ExportResults.py
  5. 0 0
      browsermob-proxy/bin/browsermob-proxy
  6. 9 0
      examples/globals_headless.json
  7. 0 4
      flask/.dockerignore
  8. 0 144
      flask/CONCEPT_DB_UI.md
  9. 0 24
      flask/Dockerfile
  10. 0 23
      flask/app/__init__.py
  11. 0 37
      flask/app/filters.py
  12. 0 132
      flask/app/forms.py
  13. 0 308
      flask/app/models.py
  14. 0 64
      flask/app/static/css/styles.css
  15. BIN
      flask/app/static/files/Testrun_2020-02-27_22-11-38.xlsx
  16. 0 190
      flask/app/static/js/testrun.js
  17. BIN
      flask/app/static/media/favicon.ico
  18. 0 60
      flask/app/templates/base.html
  19. 0 122
      flask/app/templates/testrun/create_item.html
  20. 0 25
      flask/app/templates/testrun/create_item_old.html
  21. 0 137
      flask/app/templates/testrun/edit_item.html
  22. 0 25
      flask/app/templates/testrun/edit_item_old.html
  23. 0 45
      flask/app/templates/testrun/index.html
  24. 0 100
      flask/app/templates/testrun/index_old.html
  25. 0 162
      flask/app/templates/testrun/item.html
  26. 0 425
      flask/app/templates/testrun/item_list.html
  27. 0 38
      flask/app/templates/testrun/login.html
  28. 0 43
      flask/app/templates/testrun/signup.html
  29. 0 836
      flask/app/utils.py
  30. 0 450
      flask/app/views.py
  31. 0 1
      flask/application.py
  32. 0 28
      flask/config.py
  33. 0 24
      flask/define_steps.py
  34. 0 29
      flask/define_testrun.py
  35. 0 28
      flask/discussion.txt
  36. 0 1
      flask/migrations/README
  37. 0 45
      flask/migrations/alembic.ini
  38. 0 96
      flask/migrations/env.py
  39. 0 24
      flask/migrations/script.py.mako
  40. 0 32
      flask/migrations/versions/17cfc1a6b077_.py
  41. 0 212
      flask/migrations/versions/afb044a83b98_.py
  42. 0 40
      flask/migrations/versions/c7fc082b1d55_.py
  43. 0 329
      flask/populate_db.py
  44. 0 8
      flask/requirements.txt
  45. 0 9
      flask/start_docker.sh
  46. 0 1
      flask/teststeps.json
  47. 1 0
      jsons/results_SimpleTheInternet.json
  48. 1 0
      jsons/results_example_googleImages.json
  49. 1 0
      jsons/results_paypal_secondform2.json
  50. 0 2433
      windows/baangtSetupWindows.iss
  51. 0 38
      windows/baangtWindows.spec

+ 6 - 10
.gitignore

@@ -1,19 +1,15 @@
 .idea
-/logs
-/build
-/dist
-/baangt/logs
-geckodriver.log
-/Screenshots/*.png
+logs
+build
+dist
+Screenshots
 /browserDrivers
 baangt.ini
-/Screenshots
-/docs/logs
 __pycache__
 *.pyc
 *.db
 *.log
 *.pyo
 venv
-application.sublime-workspace
-/.project
+baangt_*.xlsx
+

BIN
SimpleTheInternet.xlsx


+ 53 - 1
baangt/base/DataBaseORM.py

@@ -1,4 +1,5 @@
-from sqlalchemy import Column, String, Integer, DateTime
+from sqlalchemy import Column, String, Integer, DateTime, Boolean, Table, ForeignKey 
+from sqlalchemy.orm import relationship
 from sqlalchemy import create_engine
 from sqlalchemy.ext.declarative import declarative_base
 
@@ -7,7 +8,22 @@ DATABASE_URL = 'testrun.db'
 engine = create_engine(f'sqlite:///{DATABASE_URL}')
 base = declarative_base()
 
+#
+# declare realtion classes
+#
+class TestrunToTestcase(base):
+	__tablename__ = 'testrun_testcase'
+	testrunLog_id = Column(Integer, ForeignKey('testrunLog.id'), primary_key=True)
+	testcase_id = Column(Integer, ForeignKey('testCaseLog.id'), primary_key=True)
+
+class TestcaseToExtra(base):
+	__tablename__ = 'testcase_extras'
+	testcase_id = Column(Integer, ForeignKey('testCaseLog.id'), primary_key=True)
+	extrafield_id = Column(Integer, ForeignKey('extraFields.id'), primary_key=True)
+
+#
 # declare models
+#
 class TestrunLog(base):
 	__tablename__ = "testrunLog"
 	id = Column(Integer, primary_key=True)
@@ -20,6 +36,42 @@ class TestrunLog(base):
 	statusOk = Column(Integer, nullable=False)
 	statusFailed = Column(Integer, nullable=False)
 	statusPaused = Column(Integer, nullable=False)
+	testcases = relationship('TestCaseLog', secondary='TestrunToTestcase')
+
+class TestCaseLog(base):
+	__tablename__ = 'testCaseLog'
+	id = Column(Integer, primary_key=True)
+	toasts = Column(String, nullable=True)
+	tcErrorLog =Column(String, nullable=True)
+	vigogf = Column(String, nullable=True)
+	sapPolizzennr =Column(String, nullable=True)
+	pramie = Column(String, nullable=True)
+	polNrHost = Column(String, nullable=True)
+	testCaseStatus = Column(String, nullable=True)
+	duration = Column(String, nullable=True)
+	screenshots = Column(String, nullable=True)
+	timelog = Column(String, nullable=True)
+	exportFilesBasePath = Column(String, nullable=True)
+	tcLines = Column(String, nullable=True)
+	tcDontCloseBrowser = Column(Boolean, nullable=True)
+	tcBrowserAttributes = Column(String, nullable=True)
+	tcSlowExecution = Column(Boolean, nullable=True)
+	tcNetworkInfo = Column(Boolean, nullable=True)
+	txDebug = Column(Boolean, nullable=True)
+	screenshotPath = Column(String, nullable=True)
+	exportPath = Column(String, nullable=True)
+	importPath = Column(String, nullable=True)
+	rootPath = Column(String, nullable=True)
+	testrun = relationship('TestrunLog', secondary='TestrunToTestcase')
+	extraFields = relationship('ExtraField', secondary='TestcaseToExtra')
+
+class ExtraField(base):
+	__tablename__ = 'extraFields'
+	id = Column(Integer, primary_key=True)
+	name = Column(String, nullable=False)
+	value = Column(String, nullable=True)
+	testcase = relationship('TestCaseLog', secondary='TestcaseToExtra')
+
 
 # create tables
 base.metadata.create_all(engine)

+ 63 - 3
baangt/base/ExportResults/ExportResults.py

@@ -11,7 +11,7 @@ from xlsxwriter.worksheet import (
     Worksheet, cell_number_tuple, cell_string_tuple)
 from sqlalchemy import create_engine
 from sqlalchemy.orm import sessionmaker
-from baangt.base.DataBaseORM import DATABASE_URL, TestrunLog
+from baangt.base.DataBaseORM import DATABASE_URL, TestrunLog, TestCaseLog, ExtraField
 from datetime import datetime
 import time
 from baangt import plugin_manager
@@ -135,8 +135,8 @@ class ExportResults:
 
         # get documents
         datafiles = self.fileName
-
-        # create object
+        '''
+        # create testrun object
         log = TestrunLog(
             testrunName = self.testRunName,
             logfileName = logger.handlers[1].baseFilename,
@@ -152,6 +152,66 @@ class ExportResults:
         session.add(log)
         session.commit()
 
+        # create testcase objects
+        for testcase in self.dataRecords.values():
+            caseLog = TestCaseLog(testrun=log)
+            for key, value in testcase.items():
+                # check for known fields
+                if key == 'Toasts':
+                    caseLog.toasts = value
+                elif key == 'TCErrorLog':
+                    caseLog.tcErrorLog = value
+                elif key == 'VIGOGF#':
+                    caseLog.vigogf = value
+                elif key == 'SAP Polizzennr':
+                    caseLog.sapPolizzennr = value
+                elif key == 'Prämie':
+                    caseLog.pramie = value
+                elif key == 'PolNR Host':
+                    caseLog.polNrHost = value
+                elif key == 'TestCaseStatus':
+                    caseLog.testCaseStatus = value
+                elif key == 'Duration':
+                    caseLog.duration = value
+                elif key == 'Screenshots':
+                    caseLog.screenshots = value
+                elif key == 'timelog':
+                    caseLog.timelog = value
+                elif key == 'exportFilesBasePath':
+                    caseLog.exportFilesBasePath = value
+                elif key == 'TC.Lines':
+                    caseLog.tcLines = value
+                elif key == 'TC.dontCloseBrowser':
+                    caseLog.tcDontCloseBrowser = value
+                elif key == 'TC.BrowserAttributes':
+                    caseLog.tcBrowserAttributes = value
+                elif key == 'TC.slowExecution':
+                    caseLog.tsSlowExecution = value
+                elif key == 'TC.NetworkInfo':
+                    caseLog.tcNetworkInfo = value
+                elif key == 'TX.DEBUG':
+                    caseLog.txDebug = value
+                elif key == 'ScreenshotPath':
+                    caseLog.screenshotPath = value
+                elif key == 'ExportPath':
+                    caseLog.exportPath = value
+                elif key == 'ImportPath':
+                    caseLog.importPath = value
+                elif key == 'RootPath':
+                    caseLog.rootPath = value
+                else:
+                    # add extra field
+                    extraField = ExtraField(name=key, value=value)
+                    session.add(extraField)
+                    session.commit()
+                    caseLog.extraFields.append(extraField)
+            # save to db
+            session.add(caseLog)
+            session.commit()
+        '''               
+
+
+
     def exportResultExcel(self, **kwargs):
         self._exportData()
 

+ 0 - 0
browsermob-proxy/bin/browsermob-proxy


+ 9 - 0
examples/globals_headless.json

@@ -0,0 +1,9 @@
+{
+    "exportFilesBasePath": "",
+    "TC.Lines": "",
+    "TC.dontCloseBrowser": "True",
+    "TC.BrowserAttributes": {"HEADLESS": "True"},
+    "TC.slowExecution": "True",
+    "TC.NetworkInfo": "True",
+    "TX.DEBUG": "True"
+}

+ 0 - 4
flask/.dockerignore

@@ -1,4 +0,0 @@
-**/__pycache__
-**/*.pyc
-**/*.db
-venv

File diff suppressed because it is too large
+ 0 - 144
flask/CONCEPT_DB_UI.md


+ 0 - 24
flask/Dockerfile

@@ -1,24 +0,0 @@
-FROM python:3.7.5
-
-LABEL Testrun application
-
-ENV PYTHONDONTWRITEBYTECODE 1
-ENV FLASK_APP "application.py"
-ENV FLASK_DEBUG True
-
-RUN mkdir /testrun
-WORKDIR /testrun
-
-COPY requirements.txt requirements.txt
-
-RUN python -m venv venv
-
-RUN pip install pipenv && \
-    pipenv install --dev
-
-ADD . /testrun
-
-EXPOSE 5000
-
-RUN pipenv run python populate_db.py
-CMD pipenv run flask run --host=0.0.0.0

+ 0 - 23
flask/app/__init__.py

@@ -1,23 +0,0 @@
-import os
-from flask import Flask
-from flask_login import LoginManager
-from flask_sqlalchemy import SQLAlchemy
-from flask_migrate import Migrate
-
-app = Flask(__name__)
-
-
-#
-# configurations
-#
-app.config['SECRET_KEY'] = os.getenv('SECRET_KEY') or 'secret!key'
-app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('DATABASE_URL') or \
-    'sqlite:///' + os.path.join(os.path.abspath(os.path.dirname(__file__)), 'testrun.db')
-app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
-
-db = SQLAlchemy(app)
-migrate = Migrate(app, db)
-login_manager = LoginManager(app)
-login_manager.login_view = 'login'
-
-from app import views, models, filters

+ 0 - 37
flask/app/filters.py

@@ -1,37 +0,0 @@
-from datetime import datetime
-from . import app
-
-@app.template_filter('name_by_type')
-def item_name(item_type, plural=True):
-	#
-	# get name of the item_type
-	#
-	
-	# categories
-	if item_type == 'main':
-		name = 'Main Item'
-
-	# main items 
-	elif item_type == 'testrun':
-		name = 'Testrun'
-	elif item_type == 'testcase_sequence':
-		name = 'Test Case Sequence'
-	elif item_type == 'testcase':
-		name = 'Test Case'
-	elif item_type == 'teststep_sequence':
-		name = 'Test Step Sequence'
-	elif item_type == 'teststep':
-		name = 'Test Step'
-	else:
-		# wrong item_type
-		return ''
-
-	# check for plurals
-	if plural:
-		name += 's'
-
-	return name
-
-@app.template_filter('time')
-def format_time(time):
-	return time.strftime('%Y-%m-%d %H:%M')

+ 0 - 132
flask/app/forms.py

@@ -1,132 +0,0 @@
-from flask_wtf import FlaskForm
-from wtforms.fields import StringField, PasswordField, SelectMultipleField, SelectField, FileField
-from wtforms.widgets import TextArea, ListWidget, CheckboxInput
-from wtforms.validators import ValidationError, DataRequired, EqualTo
-from app.models import User
-from app import utils
-
-
-#
-# authantication forms
-#
-
-class LoginForm(FlaskForm):
-	username = StringField('Username', validators=[DataRequired()])
-	password = PasswordField('Password', validators=[DataRequired()])
-
-	def validate_username(self, username):
-		if User.query.filter_by(username=username.data).count() == 0:
-			raise ValidationError('Wrong username')
-
-	def validate_password(self, password):
-		user = User.query.filter_by(username=self.username.data).first()
-		if user and not user.verify_password(password.data):
-			raise ValidationError('Wrong password')
-
-class SingupForm(FlaskForm):
-	username = StringField('Username', validators=[DataRequired()])
-	password = PasswordField('Password', validators=[DataRequired()])
-	password2 = PasswordField('Password again', validators=[DataRequired(), EqualTo('password')])
-
-	def validate_username(self, username):
-		if User.query.filter_by(username=username.data).count() > 0:
-			raise ValidationError('This username is in use. Please try another username.')
-
-#
-# testrun items forms
-#
-
-class TestrunImportForm(FlaskForm):
-	file = FileField('Source', validators=[DataRequired()])
-
-
-
-class TestrunCreateForm(FlaskForm):
-	name = StringField('Name', validators=[DataRequired()])
-	description = StringField('Description', validators=[DataRequired()], widget=TextArea())
-	testcase_sequences = SelectMultipleField('Test Case Sequences')
-
-	@classmethod
-	def new(cls):
-		# update choices
-		form = cls()
-		form.testcase_sequences.choices = utils.getTestCaseSequences()
-		return form
-
-
-class TestCaseSequenceCreateForm(FlaskForm):
-	name = StringField('Name', validators=[DataRequired()])
-	description = StringField('Description', validators=[DataRequired()], widget=TextArea())
-	classname = SelectField('Class Name')
-	datafiles = SelectMultipleField('Data Files')
-	testcases = SelectMultipleField('Test Cases')
-
-	@classmethod
-	def new(cls):
-		# update choices
-		form = cls()
-		form.classname.choices = utils.getClassNames()
-		form.datafiles.choices = utils.getDataFiles()
-		form.testcases.choices = utils.getTestCases()
-		return form
-
-
-class TestCaseCreateForm(FlaskForm):
-	name = StringField('Name', validators=[DataRequired()])
-	description = StringField('Description', validators=[DataRequired()], widget=TextArea())
-	classname = SelectField('Class Name')
-	browser_type = SelectField('Browser Type')
-	testcase_type = SelectField('Test Case Type')
-	testcase_stepsequences = SelectMultipleField('Step Sequences')
-
-	@classmethod
-	def new(cls):
-		# update choices
-		form = cls()
-		form.classname.choices = utils.getClassNames()
-		form.browser_type.choices = utils.getBrowserTypes()
-		form.testcase_type.choices = utils.getTestCaseTypes()
-		form.testcase_stepsequences.choices = utils.getTestStepSequences()
-		return form
-
-
-class TestStepSequenceCreateForm(FlaskForm):
-	name = StringField('Name', validators=[DataRequired()])
-	description = StringField('Description', validators=[DataRequired()], widget=TextArea())
-	classname = SelectField('Class Name')
-	teststeps = SelectMultipleField('Test Steps')
-
-	@classmethod
-	def new(cls):
-		# update choices
-		form = cls()
-		form.classname.choices = utils.getClassNames()
-		form.teststeps.choices = utils.getTestSteps()
-		return form
-
-
-class TestStepCreateForm(FlaskForm):
-	name = StringField('Name', validators=[DataRequired()])
-	description = StringField('Description', validators=[DataRequired()], widget=TextArea())
-	activity_type = SelectField('Activity Type')
-	locator_type = SelectField('Locator Type')
-	# model extension
-	locator = StringField('Locator')
-	value = StringField('Value')
-	value2 = StringField('Value 2')
-	comparision = SelectField('Comparision')
-	optional = SelectField('Optional', choices=(('1', 'False'), ('2', 'True')))
-	timeout = StringField('Timeout')
-	release = StringField('Release')
-	
-
-	@classmethod
-	def new(cls):
-		# update choices
-		form = cls()
-		form.activity_type.choices = utils.getActivityTypes()
-		form.locator_type.choices = utils.getLocatorTypes()
-		form.comparision.choices = utils.getComparisionChoices()
-		return form
-
-	

+ 0 - 308
flask/app/models.py

@@ -1,308 +0,0 @@
-from flask_sqlalchemy import SQLAlchemy
-from flask_login import UserMixin
-from werkzeug.security import generate_password_hash, check_password_hash
-from datetime import datetime
-from app import db, login_manager
-
-#
-# user Model
-# TODO: extend user Model
-#
-class User(UserMixin, db.Model):
-	__tablename__ = 'users'
-	id = db.Column(db.Integer, primary_key=True)
-	username = db.Column(db.String(64), unique=True, nullable=False)
-	password = db.Column(db.String(128), nullable=False)
-	created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
-	lastlogin = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
-
-	def __str__(self):
-		return self.username
-
-	def set_password(self, password):
-		self.password = generate_password_hash(password)
-
-	def verify_password(self, password):
-		return check_password_hash(self.password, password)
-
-@login_manager.user_loader
-def load_user(id):
-	return User.query.get(int(id))
-
-#
-# relation tables
-#
-
-testrun_casesequence = db.Table(
-	'testrun_casesequence',
-	db.Column('testrun_id', db.Integer, db.ForeignKey('testruns.id'), primary_key=True),
-	db.Column('testcase_sequence_id', db.Integer, db.ForeignKey('testcase_sequences.id'), primary_key=True)
-)
-
-testcase_sequence_datafile = db.Table(
-	'testcase_sequence_datafile',
-	db.Column('testcase_sequence_id', db.Integer, db.ForeignKey('testcase_sequences.id'), primary_key=True),
-	db.Column('datafile_id', db.Integer, db.ForeignKey('datafiles.id'), primary_key=True)
-)
-
-testcase_sequence_case = db.Table(
-	'testcase_sequence_case',
-	db.Column('testcase_sequence_id', db.Integer, db.ForeignKey('testcase_sequences.id'), primary_key=True),
-	db.Column('testcase_id', db.Integer, db.ForeignKey('testcases.id'), primary_key=True)
-)
-
-testcase_stepsequence = db.Table(
-	'testcase_stepsequence',
-	db.Column('testcase_id', db.Integer, db.ForeignKey('testcases.id'), primary_key=True),
-	db.Column('teststep_sequence_id', db.Integer, db.ForeignKey('teststep_sequences.id'), primary_key=True)
-)
-
-
-#
-# main entities
-#
-class Testrun(db.Model):
-	__tablename__ = 'testruns'
-	id = db.Column(db.Integer, primary_key=True)
-	name = db.Column(db.String(64), nullable=False)
-	description = db.Column(db.String(512), nullable=False)
-	created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
-	creator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
-	edited = db.Column(db.DateTime, nullable=True)
-	editor_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
-	creator = db.relationship('User', backref='created_testruns', lazy='immediate', foreign_keys=[creator_id])
-	editor = db.relationship('User', backref='edited_testruns', lazy='immediate', foreign_keys=[editor_id])
-
-	def __str__(self):
-		return self.name
-
-	def to_json(self):
-		return {
-			'TestCaseSequence' : [x.to_json() for x in self.testcase_sequences],
-		}
-
-class TestCaseSequence(db.Model):
-	__tablename__ = 'testcase_sequences'
-	id = db.Column(db.Integer, primary_key=True)
-	name = db.Column(db.String(64), nullable=False)
-	description = db.Column(db.String(512), nullable=False)
-	created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
-	creator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
-	edited = db.Column(db.DateTime, nullable=True)
-	editor_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
-	classname_id = db.Column(db.Integer, db.ForeignKey('classnames.id'), nullable=False)
-	creator = db.relationship('User', backref='created_testcase_sequances', lazy='immediate', foreign_keys=[creator_id])
-	editor = db.relationship('User', backref='edited_testcase_sequances', lazy='immediate', foreign_keys=[editor_id])
-	classname = db.relationship('ClassName', backref='testcase_sequences', lazy='immediate', foreign_keys=[classname_id])
-	testrun = db.relationship(
-		'Testrun',
-		secondary=testrun_casesequence,
-		lazy='subquery',
-		backref=db.backref('testcase_sequences', lazy=True),
-	)
-
-	def __str__(self):
-		return self.name
-
-	def to_json(self):
-		if len(self.datafiles) > 0:
-			TestDataFileName = self.datafiles[0].filename
-		else:
-			TestDataFileName = ''
-		return {
-			'SequenceClass': self.classname.name,
-			'TestDataFileName': TestDataFileName,
-			'TestCase' : [x.to_json() for x in self.testcases],
-		}
-
-class DataFile(db.Model):
-	__tablename__ = 'datafiles'
-	id = db.Column(db.Integer, primary_key=True)
-	filename = db.Column(db.String(64), nullable=False)
-	created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
-	creator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
-	creator = db.relationship('User', backref='created_datafiles', lazy='immediate', foreign_keys=[creator_id])
-	testcase_sequence = db.relationship(
-		'TestCaseSequence',
-		secondary=testcase_sequence_datafile,
-		lazy='subquery',
-		backref=db.backref('datafiles', lazy=True),
-	)
-
-	def __str__(self):
-		return self.filename
-
-class TestCase(db.Model):
-	__tablename__ = 'testcases'
-	id = db.Column(db.Integer, primary_key=True)
-	name = db.Column(db.String(64), nullable=False)
-	description = db.Column(db.String(512), nullable=False)
-	created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
-	creator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
-	edited = db.Column(db.DateTime, nullable=True)
-	editor_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
-	classname_id = db.Column(db.Integer, db.ForeignKey('classnames.id'), nullable=False)
-	browser_type_id = db.Column(db.Integer, db.ForeignKey('browser_types.id'), nullable=False)
-	testcase_type_id = db.Column(db.Integer, db.ForeignKey('testcase_types.id'), nullable=False)
-	creator = db.relationship('User', backref='created_testcases', lazy='immediate', foreign_keys=[creator_id])
-	editor = db.relationship('User', backref='edited_testcases', lazy='immediate', foreign_keys=[editor_id])
-	classname = db.relationship('ClassName', backref='testcases', lazy='immediate', foreign_keys=[classname_id])
-	browser_type = db.relationship('BrowserType', backref='testcases', lazy='immediate', foreign_keys=[browser_type_id])
-	testcase_type = db.relationship('TestCaseType', backref='testcases', lazy='immediate', foreign_keys=[testcase_type_id])
-	testcase_sequence = db.relationship(
-		'TestCaseSequence',
-		secondary=testcase_sequence_case,
-		lazy='subquery',
-		backref=db.backref('testcases', lazy=True),
-	)
-
-	def __str__(self):
-		return self.name
-
-	def to_json(self):
-		return {
-			'TestCaseClass': self.classname.name,
-			'TestCaseType': self.testcase_type.name,
-			'Browser': self.browser_type.name,
-			'TestStepSequences' : [x.to_json() for x in self.teststep_sequences],
-		}
-
-class TestStepSequence(db.Model):
-	__tablename__ = 'teststep_sequences'
-	id = db.Column(db.Integer, primary_key=True)
-	name = db.Column(db.String(64), nullable=False)
-	description = db.Column(db.String(512), nullable=False)
-	created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
-	creator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
-	edited = db.Column(db.DateTime, nullable=True)
-	editor_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
-	classname_id = db.Column(db.Integer, db.ForeignKey('classnames.id'), nullable=False)
-	creator = db.relationship('User', backref='created_teststep_sequences', lazy='immediate', foreign_keys=[creator_id])
-	editor = db.relationship('User', backref='edited_teststep_sequences', lazy='immediate', foreign_keys=[editor_id])
-	classname = db.relationship('ClassName', backref='teststep_sequences', lazy='immediate', foreign_keys=[classname_id])
-	testcase = db.relationship(
-		'TestCase',
-		secondary=testcase_stepsequence,
-		lazy='subquery',
-		backref=db.backref('teststep_sequences', lazy=True),
-	)
-
-	def __str__(self):
-		return self.name
-
-	def to_json(self):
-		return {
-			'TestStepClass': self.classname.name,
-			'TestSteps' : [x.to_json() for x in self.teststeps],
-		}
-
-class TestStepExecution(db.Model):
-	__tablename__ = 'teststep_executions'
-	id = db.Column(db.Integer, primary_key=True)
-	name = db.Column(db.String(64), nullable=False)
-	description = db.Column(db.String(512), nullable=False)
-	created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
-	creator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
-	edited = db.Column(db.DateTime, nullable=True)
-	editor_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
-	activity_type_id = db.Column(db.Integer, db.ForeignKey('activity_types.id'), nullable=False)
-	locator_type_id = db.Column(db.Integer, db.ForeignKey('locator_types.id'), nullable=True)
-	teststep_sequence_id = db.Column(db.Integer, db.ForeignKey('teststep_sequences.id'), nullable=True)
-	creator = db.relationship('User', backref='created_teststeps', lazy='immediate', foreign_keys=[creator_id])
-	editor = db.relationship('User', backref='edited_teststeps', lazy='immediate', foreign_keys=[editor_id])
-	activity_type = db.relationship('ActivityType', backref='teststeps', lazy='immediate', foreign_keys=[activity_type_id])
-	locator_type = db.relationship('LocatorType', backref='teststeps', lazy='immediate', foreign_keys=[locator_type_id])
-	teststep_sequence = db.relationship('TestStepSequence', backref='teststeps', lazy='immediate', foreign_keys=[teststep_sequence_id])
-	# model extension
-	locator = db.Column(db.String, nullable=True)
-	optional = db.Column(db.Boolean, nullable=False, default=False)
-	timeout = db.Column(db.Float(precision='5,2'), nullable=True)
-	release = db.Column(db.String, nullable=True)
-	value = db.Column(db.String, nullable=True)
-	value2 = db.Column(db.String, nullable=True)
-	comparision = db.Column(db.String, nullable=True)
-
-	def __str__(self):
-		return self.name
-
-	def to_json(self):
-		return {
-			'Activity': self.activity_type.name,
-			'LocatorType': self.locator_type.name,
-			'Locator': self.locator,
-			'Value': self.value,
-			'Comparison': self.comparision,
-			'Value2': self.value2,
-			'Timeout': self.timeout,
-			'Optional': self.optional,
-			'Release': self.release,
-		}
-
-#
-# supporting entities
-#
-class GlobalTestStepExecution(db.Model):
-	__tablename__ = 'global_teststep_executions'
-	id = db.Column(db.Integer, primary_key=True)
-	name = db.Column(db.String(64), nullable=False)
-	description = db.Column(db.String(512), nullable=False)
-	created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
-	creator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
-	edited = db.Column(db.DateTime, nullable=True)
-	editor_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
-	activity_type_id = db.Column(db.Integer, db.ForeignKey('activity_types.id'), nullable=False)
-	locator_type_id = db.Column(db.Integer, db.ForeignKey('locator_types.id'), nullable=False)
-	teststep_sequence_id = db.Column(db.Integer, db.ForeignKey('teststep_sequences.id'), nullable=True)
-	creator = db.relationship('User', backref='created_global_teststeps', lazy='immediate', foreign_keys=[creator_id])
-	editor = db.relationship('User', backref='edited_global_teststeps', lazy='immediate', foreign_keys=[editor_id])
-	activity_type = db.relationship('ActivityType', backref='global_teststeps', lazy='immediate', foreign_keys=[activity_type_id])
-	locator_type = db.relationship('LocatorType', backref='global_teststeps', lazy='immediate', foreign_keys=[locator_type_id])
-	teststep_sequence = db.relationship('TestStepSequence', backref='global_teststeps', lazy='immediate', foreign_keys=[teststep_sequence_id])
-	
-	def __str__(self):
-		return self.name
-
-class ClassName(db.Model):
-	__tablename__ = 'classnames'
-	id = db.Column(db.Integer, primary_key=True)
-	name = db.Column(db.String(64), nullable=False)
-	description = db.Column(db.String(512), nullable=False)
-
-	def __str__(self):
-		return self.name
-
-class BrowserType(db.Model):
-	__tablename__ = 'browser_types'
-	id = db.Column(db.Integer, primary_key=True)
-	name = db.Column(db.String(64), nullable=False)
-	description = db.Column(db.String(512), nullable=False)
-
-	def __str__(self):
-		return self.name
-	
-class TestCaseType(db.Model):
-	__tablename__ = 'testcase_types'
-	id = db.Column(db.Integer, primary_key=True)
-	name = db.Column(db.String(64), nullable=False)
-	description = db.Column(db.String(512), nullable=False)
-
-	def __str__(self):
-		return self.name
-
-class ActivityType(db.Model):
-	__tablename__ = 'activity_types'
-	id = db.Column(db.Integer, primary_key=True)
-	name = db.Column(db.String(64), nullable=False)
-	description = db.Column(db.String(512), nullable=False)
-
-	def __str__(self):
-		return self.name
-
-class LocatorType(db.Model):
-	__tablename__ = 'locator_types'
-	id = db.Column(db.Integer, primary_key=True)
-	name = db.Column(db.String(64), nullable=False)
-	description = db.Column(db.String(512), nullable=False)
-
-	def __str__(self):
-		return self.name

+ 0 - 64
flask/app/static/css/styles.css

@@ -1,64 +0,0 @@
-body {
-  min-width: 420px;
-}
-
-/* chips */
-.chip {
-  display: inline-block;
-  padding: 0 10px;
-  height: 30px;
-  font-size: 16px;
-  /*line-height: 50px;*/
-  border-radius: 15px;
-  background-color: #f1f1f1;
-}
-
-.closebtn {
-  padding-left: 10px;
-  color: #888;
-  font-weight: bold;
-  float: right;
-  font-size: 20px;
-  cursor: pointer;
-}
-
-/* input groups */
-.closebtn:hover {
-  color: #000;
-}
-
-.fixed-label {
-  width: 11rem;
-}
-
-.text-break {
-  word-wrap: break-word;
-}
-
-/* buttons */
-.btn-right {
-  width: 10rem;
-}
-
-/* dialog */
-.dialog {
-  justify-content: center !important;
-}
-
-
-/* widths */
-.w-20 {
-  width: 20%;
-}
-
-.w-40 {
-  width: 40%;
-}
-
-.w-60 {
-  width: 60%;
-}
-
-.w-80 {
-  width: 80%;
-}

BIN
flask/app/static/files/Testrun_2020-02-27_22-11-38.xlsx


+ 0 - 190
flask/app/static/js/testrun.js

@@ -1,190 +0,0 @@
-/*   document.addEventListener('DOMContentLoaded', () => {
-        // set links
-        document.querySelectorAll('.dropdown-item').forEach(link => {
-            link.onclick = () => {
-                // load item data to main
-                load_item(link.dataset.type, link.dataset.id);
-                return false;
-            };
-        });
-
-        document.querySelectorAll('.collapse>.btn').forEach(link => {
-            link.onclick = () => {
-                // create item
-                new_item(link.dataset.type)
-                //document.querySelector('main').innerHTML = "Done!"
-            };
-        });
-
-    });
-*/
-    // get item
-    function load_item(item_type, item_id) {
-        const request = new XMLHttpRequest();
-        request.open('GET', `/${item_type}/${item_id}`);
-        request.onload = () => {
-            const response = request.responseText;
-            document.querySelector('main').innerHTML = response;
-        };
-        request.send();
-    }
-
-    // create new item
-    function new_item(item_type) {
-        const request = new XMLHttpRequest();
-        request.open('GET', `/${item_type}/new`);
-        request.onload = () => {
-            const response = request.responseText;
-            document.querySelector('main').innerHTML = response;
-        };
-        request.send();
-    }
-
-    // edit item
-    function edit_item(item_type, item_id) {
-        const request = new XMLHttpRequest();
-        request.open('GET', `/${item_type}/${item_id}/edit`);
-        request.onload = () => {
-            const response = request.responseText;
-            document.querySelector('main').innerHTML = response;
-        };
-        request.send();
-    }
-
-    // delete item
-    function set_delete_item(type_name, type, item_id) {
-        /* set data in delete modal */
-        document.querySelector('#deleteLabel').innerHTML = `Delete ${type_name} ID #${item_id}`;
-        const btns = document.querySelector('#deleteButtons');
-        btns.setAttribute('data-type', type);
-        btns.setAttribute('data-id', item_id);
-    }
-
-    function delete_item(e, cascade) {
-        /* delete item */
-        const request = new XMLHttpRequest();
-        if (cascade) {
-            // cascade delete: POST request
-            request.open('POST', `/${e.dataset['type']}/${e.dataset['id']}/delete`);
-        } else {
-            // single item delete: DELETE request
-            request.open('DELETE', `/${e.dataset['type']}/${e.dataset['id']}/delete`);
-        }
-        
-        request.onload = () => {
-            window.location.reload(true); 
-        }
-        request.send();
-    }
-
-    // select multiple with chips
-    function add_chip(e, name) {
-        // check if option exists
-        const text = e.value;
-        for (var i = 0; i < e.list.childElementCount; i++) {
-            const opt_text = e.list.children[i].text.toUpperCase();
-            if (text === e.list.children[i].text) {
-                // create chip
-                var chip_area = document.getElementById(`chips_${name}`);
-                var new_chip = document.createElement('div');
-                new_chip.setAttribute('class', 'chip mr-1');
-                new_chip.setAttribute('data-id', i);
-                new_chip.innerHTML = `
-                    <small>${e.list.children[i].text}</small>
-                    <span class="closebtn" onclick="delete_chip(this.parentElement, '${name}')">&times;</span>
-                    `;
-                chip_area.appendChild(new_chip);
-                // remove selected option from list
-                e.value = null;
-                e.list.children[i].disabled = true;
-            }
-        }
-    }
-
-    function delete_chip(e, name) {
-        var list = document.getElementById(`${name}Opt`);
-        list.children[e.dataset['id']].disabled = false;
-        e.parentElement.removeChild(e);
-    }
-
-    function get_chips() {
-        document.querySelectorAll('datalist').forEach(list => {
-            // create multyselect field
-            const name = list.id.substring(0, list.id.length - 3);
-            const selector = document.getElementById(name);
-            const chips_area = document.getElementById(`chips_${name}`);
-            chips_area.querySelectorAll('div').forEach(chip => {
-                value = chip.dataset['id'];
-                selector.options[value].selected = true;
-            });
-        });
-
-        return false;
-    }
-
-    function filter_options(e) {
-        const text = e.value.toUpperCase();
-        for (var i = 0; i < e.list.childElementCount; i++) {
-            const opt_text = e.list.children[i].text.toUpperCase();
-            if (!opt_text.includes(text)) {
-                e.list.children[i].style.display = opt_text ? 'list-item' : 'none';
-            }
-
-
-        }
-    }
-
-    function filter_items(e) {
-        const text = e.value.toUpperCase();
-        document.querySelectorAll('.testrun-item').forEach(item => {
-            var display = false;
-            item.querySelectorAll('.filtered').forEach(property => {
-                const value = property.innerHTML.toUpperCase();
-                if (value.includes(text)) {
-                    display = true;
-                }
-            });
-
-            item.style.display = display ? '' : 'none';
-
-        });
-    }
-
-
-    function get_file(e) {
-        // get filename
-        const filename = e.files[0].name;
-        const label = e.parentElement.querySelector('label');
-        label.innerText = filename;
-    }
-
-    function set_export_id(item_id) {
-        // set item_id in modal
-        const btn = document.querySelector('#exportButton')
-        btn.setAttribute('data-id', item_id);
-        btn.style.display = '';
-        // set modal body
-        document.querySelector('#exportRequest').style.display = '';
-        document.querySelector('#exportResponse').style.display = 'none';
-    }
-
-    function exportTestrun(e) {
-        const request = new XMLHttpRequest();
-        request.open('GET', `/testrun/xlsx/${e.dataset['id']}`);
-        request.onload = () => {
-            // get request
-            const response = request.responseText;
-            // set export modal body 
-            const exportResponse = document.querySelector('#exportResponse');
-            exportResponse.innerHTML = response;
-            exportResponse.style.display = '';
-            document.querySelector('#exportRequest').style.display = 'none';
-            document.querySelector('#exportButton').style.display = 'none';
-        };
-        request.send();
-    }
-
-    function set_update_item(item_id) {
-        document.querySelector('#updateLabel').innerHTML = `Update Testrun ID #${item_id}`;
-        document.querySelector('#updateForm').setAttribute('action', `/testrun/${item_id}/import`);
-    }

BIN
flask/app/static/media/favicon.ico


+ 0 - 60
flask/app/templates/base.html

@@ -1,60 +0,0 @@
-<!doctype html>
-<html lang="en">
-  <head>
-    <!-- Required meta tags -->
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
-
-    <!-- Bootstrap CSS -->
-    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
-
-    <!-- Local Styles -->
-    <link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
-
-    <!-- favicon -->
-    <link rel="shortcut icon" href="{{ url_for('static', filename='media/favicon.ico') }}">
-
-    <title>
-    	{% block title %}
-    	{% endblock %}
-    </title>
-  </head>
-  
-  <body>
-    <nav class="navbar navbar-expand navbar-dark bg-dark flex-md-nowrap p-1 shadow">
-        <a class="navbar-brand ml-3 py-2" href="/">
-            <img src="{{ url_for('static', filename='media/favicon.ico') }}" width="40" height="40" class="rounded" alt="">
-            Testruns Definition
-        </a>
-        <ul class="navbar-nav ml-auto mr-3">            
-            <li class="nav-item">
-                {% if current_user.is_authenticated %}
-                    <span class="nav-link">{{ current_user }}</span>
-                {% else %}
-                    <a class="nav-link" href="{{ url_for('login')}}">Login</a>
-                {% endif %}
-            </li>
-            <li class="nav-item">
-                {% if current_user.is_authenticated %}
-                    <a class="nav-link" href="{{ url_for('logout')}}">Sign out</a>
-                {% else %}
-                    <a class="nav-link" href="{{ url_for('signup')}}">Sign up</a>
-                {% endif %}
-            </li>
-        </ul>
-    </nav>
-      
-    {% block content %}
-    {% endblock %}
-
-
-    <!-- Local JS -->
-    <script src="{{ url_for('static', filename='js/testrun.js') }}"></script>
-
-    <!-- Optional JavaScript -->
-    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
-    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
-    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
-    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
-  </body>
-</html>

+ 0 - 122
flask/app/templates/testrun/create_item.html

@@ -1,122 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}
-    Create {{ type|name_by_type(False) }}
-{% endblock %}
-
-{% block content %}
-
-<main>
-
-<!-- bread crumb -->
-<nav aria-label="breadcrumb">
-    <ol class="breadcrumb">
-        <li class="breadcrumb-item"><a href="/">Home</a></li>
-        <li class="breadcrumb-item"><a href="/{{ type }}">{{ type|name_by_type }}</a></li>
-        <li class="breadcrumb-item active" aria-current="page">New</li>
-    </ol>
-</nav>
-
-<!-- flash messages -->
-{% with messages = get_flashed_messages(with_categories=true) %}
-    {% for category, msg in messages %}
-        <div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">
-            {{ msg }}
-            <button type="button" class="close" data-dismiss="alert" aria-label="Close">
-                <span aria-hidden="true">&times;</span>
-            </button>
-        </div>
-    {% endfor %}
-{% endwith %}
-
-<div class="container mt-5">
-    <div class="border-bottom pt-3">
-        <h1 >Create new {{ type|name_by_type(False) }}</h1>
-    </div>
-
-    <form class="pr-5" method="post" onsubmit="get_chips()" action="/{{ type }}/new">
-        {{ form.hidden_tag() }}
-
-        {% for field in form %}
-            {% if field.name != "csrf_token" %}
-            
-            <!-- error message -->
-            {% for error in field.errors %}
-                <p class="text-danger">{{ error }}</p>
-            {% endfor %}
-
-            {% if field.name == 'value' %}
-                <div class="input-group mt-3">
-                    <div class="input-group-prepend w-60 ml-0">
-                        {{ form.value.label(class="input-group-text w-80") }}
-                        {{ form.comparision.label(class="input-group-text w-40") }}
-                    </div>
-                    <div class="input-group-append w-40">
-                        {{ form.value2.label(class="input-group-text w-100") }}
-                    </div>
-                </div>
-                <div class="input-group mb-3">
-                    <div class="input-group w-40">
-                        {{ form.value(class="form-control w-50") }}
-                    </div>
-                    <div class="input-group w-20">
-                        {{ form.comparision(class="form-control") }}
-                    </div>
-                    <div class="input-group w-40">
-                        {{ form.value2(class="form-control") }}                    
-                    </div>
-                </div>
-
-            {% elif field.name != 'comparision' and field.name != 'value2' %}
-            <div class="input-group my-3">
-                <div class="input-group-prepend">
-                    {{ field.label(class="input-group-text fixed-label") }}
-                </div>
-
-                {% if field.name in chips %}
-                <!-- choices for chips -->
-                    <input type="text" class="form-control" list="{{ field.name }}Opt" data-name="{{ field.name }}"
-                        onkeyup="filter_options(this)" oninput="add_chip(this, '{{ field.name }}')">
-                    <datalist id="{{ field.name }}Opt">
-                        {% for option in field.choices %}
-                            <option>
-                                {{ option.1 }}
-                            </option>
-                        {% endfor %}
-                    </datalist>
-                
-                {% else %}
-                <!-- other fields -->
-                    {{ field(class="form-control") }}
-                {% endif %}
-            </div>
-
-            <!-- chips area -->
-            {% if field.name in chips %}
-                <!-- hidden multi-select -->
-                <select class="d-none" id="{{ field.name }}" name="{{ field.name }}" multiple>
-                    {% for option in field.choices %}
-                        <option value="{{ option.0 }}">
-                            {{ option.1 }}
-                        </option>
-                    {% endfor %}
-                </select>
-                
-                <!-- chips -->
-                <div id="chips_{{ field.name }}">
-                </div>
-            {% endif %}
-            {% endif %}
-            {% endif %}
-
-        {% endfor %}
-
-        <p>
-            <button class="btn btn-primary px-5 my-3" type="submit">Create</button>
-        </p>
-    </form>
-</div>
-
-</main>
-
-{% endblock %}

+ 0 - 25
flask/app/templates/testrun/create_item_old.html

@@ -1,25 +0,0 @@
-<div class="border-bottom pt-3">
-    <h1 >Create a New {{ type|capitalize }}</h1>
-</div>
-
-<form class="pr-5" method="post" action="/{{ type }}/new">
-    {{ form.hidden_tag() }}
-
-    {% for field in form %}
-        {% if field.name != "csrf_token" %}
-        <div class="input-group my-3">
-            {% for error in field.errors %}
-                <p class="text-danger">{{ error }}</p>
-            {% endfor %}
-            <div class="input-group-prepend">
-                {{ field.label(class="input-group-text") }}
-            </div>
-            {{ field(class="form-control")}}
-        </div>
-        {% endif %}
-    {% endfor %}
-
-    <p>
-        <button class="btn btn-primary px-5 my-3" type="submit">Create</button>
-    </p>
-</form>

+ 0 - 137
flask/app/templates/testrun/edit_item.html

@@ -1,137 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}
-    {{ item.name }}
-{% endblock %}
-
-{% block content %}
-
-<main>
-
-<!-- bread crumb -->
-<nav aria-label="breadcrumb">
-    <ol class="breadcrumb">
-        <li class="breadcrumb-item"><a href="/">Home</a></li>
-        <li class="breadcrumb-item"><a href="/{{ type }}">{{ type|name_by_type }}</a></li>
-        <li class="breadcrumb-item active" aria-current="page">{{ item.name }}</li>
-    </ol>
-</nav>
-
-<!-- flash messages -->
-{% with messages = get_flashed_messages(with_categories=true) %}
-    {% for category, msg in messages %}
-        <div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">
-            {{ msg }}
-            <button type="button" class="close" data-dismiss="alert" aria-label="Close">
-                <span aria-hidden="true">&times;</span>
-            </button>
-        </div>
-    {% endfor %}
-{% endwith %}
-
-<div class="container mt-5">
-    <div class="border-bottom pt-3">
-        <h1 >Edit '{{ item.name }}'</h1>
-    </div>
-
-    <form class="pr-5" method="post" onsubmit="get_chips()" action="/{{ type }}/{{ item.id }}/edit">
-        {{ form.hidden_tag() }}
-
-        {% for field in form %}
-            {% if field.name != "csrf_token" %}
-            
-            <!-- show errors -->
-            {% for error in field.errors %}
-                <p class="text-danger">{{ error }}</p>
-            {% endfor %}
-
-            {% if field.name == 'value' %}
-                <div class="input-group mt-3">
-                    <div class="input-group-prepend w-60 ml-0">
-                        {{ form.value.label(class="input-group-text w-80") }}
-                        {{ form.comparision.label(class="input-group-text w-40") }}
-                    </div>
-                    <div class="input-group-append w-40">
-                        {{ form.value2.label(class="input-group-text w-100") }}
-                    </div>
-                </div>
-                <div class="input-group mb-3">
-                    <div class="input-group w-40">
-                        {{ form.value(class="form-control w-50", value=form.value.data) }}
-                    </div>
-                    <div class="input-group w-20">
-                        {{ form.comparision(class="form-control", value=form.comparision.data) }}
-                    </div>
-                    <div class="input-group w-40">
-                        {{ form.value2(class="form-control", value=form.value2.data) }}                    
-                    </div>
-                </div>
-            {% elif field.name != 'comparision' and field.name != 'value2' %}
-            <div class="input-group my-3">
-                <div class="input-group-prepend">
-                    {{ field.label(class="input-group-text fixed-label") }}
-                </div>
-
-                {% if field.data is iterable and field.data is not string %}
-                <!-- choices for chips -->
-                    <input type="text" class="form-control" list="{{ field.name }}Opt" data-name="{{ field.name }}"
-                        onkeyup="filter_options(this)" oninput="add_chip(this, '{{ field.name }}')">
-                    <datalist id="{{ field.name }}Opt">
-                        {% for option in field.choices %}
-                            <option {% if option.0 in field.data %}disabled{% endif %}>
-                                {{ option.1 }}
-                            </option>
-                        {% endfor %}
-                    </datalist>
-<!--
-                    <select class="form-control multyselect" id="{{ field.name }}" name="{{ field.name }}"
-                    onchange="add_chip(this, '{{ field.name }}')">
-                        <option value="0" select="">&lt; Select &gt;</option>
-                        {% for option in field.choices %}
-                            <option value="{{ option.0 }}" {% if option.0 in field.data %}disabled{% endif %}>
-                                {{ option.1 }}
-                            </option>
-                        {% endfor %}
-                    </select>
--->                
-                {% else %}
-                <!-- other fields -->
-                    {{ field(class="form-control", value=field.data) }}
-                {% endif %}
-            </div>
-
-            <!-- chips -->
-            {% if field.data is iterable and field.data is not string %}
-                <!-- multi-select -->
-                <select class="d-none" id="{{ field.name }}" name="{{ field.name }}" multiple>
-                    {% for option in field.choices %}
-                        <option value="{{ option.0 }}">
-                            {{ option.1 }}
-                        </option>
-                    {% endfor %}
-                </select>
-
-                <!-- chips -->
-                <div id="chips_{{ field.name }}">
-                    {% for option in field.choices if option.0 in field.data %}
-                        <div class="chip mr-1" data-id="{{ option.0|int - 1 }}">
-                            <small>{{ option.1 }}</small>
-                            <span class="closebtn" onclick="delete_chip(this.parentElement, '{{ field.name }}')">&times;</span>
-                        </div>
-                    {% endfor %}
-                </div>
-            {% endif %}
-            {% endif %}
-            {% endif %}
-
-        {% endfor %}
-
-        <p>
-            <button class="btn btn-primary px-5 my-3" type="submit">Save</button>
-        </p>
-    </form>
-</div>
-
-</main>
-
-{% endblock %}

+ 0 - 25
flask/app/templates/testrun/edit_item_old.html

@@ -1,25 +0,0 @@
-<div class="border-bottom pt-3">
-    <h1 >Edit '{{ item.name }}'</h1>
-</div>
-
-<form class="pr-5" method="post" action="/{{ type }}/{{ item.id }}/edit">
-    {{ form.hidden_tag() }}
-
-    {% for field in form %}
-        {% if field.name != "csrf_token" %}
-        <div class="input-group my-3">
-            {% for error in field.errors %}
-                <p class="text-danger">{{ error }}</p>
-            {% endfor %}
-            <div class="input-group-prepend">
-                {{ field.label(class="input-group-text") }}
-            </div>
-            {{ field(class="form-control", value=field.data)}}
-        </div>
-        {% endif %}
-    {% endfor %}
-
-    <p>
-        <button class="btn btn-primary px-5 my-3" type="submit">Save</button>
-    </p>
-</form>

+ 0 - 45
flask/app/templates/testrun/index.html

@@ -1,45 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}
-    Home
-{% endblock %}
-
-{% block content %}
-
-<!-- bread crumb -->
-<nav aria-label="breadcrumb">
-    <ol class="breadcrumb">
-        <li class="breadcrumb-item active" aria-current="page">Home</li>
-    </ol>
-</nav>
-
-<!-- flash messages -->
-{% with messages = get_flashed_messages(with_categories=true) %}
-    {% for category, msg in messages %}
-        <div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">
-            {{ msg }}
-            <button type="button" class="close" data-dismiss="alert" aria-label="Close">
-                <span aria-hidden="true">&times;</span>
-            </button>
-        </div>
-    {% endfor %}
-{% endwith %}
-
-<!-- item list -->
-<div class="container mt-5">
-    <div class="row">
-        <div class="list-group col-12">
-            {% for category, item_list in items.items() %}
-                <!-- header -->
-                <p class="list-group-item active h4">{{ category|name_by_type }}</p>
-                
-                <!-- items -->
-                {% for item in item_list %}
-                    <a class="list-group-item list-group-item-action h5" href="/{{ item }}">{{ item|name_by_type }}</a>
-                {% endfor %}
-            {% endfor %}
-        </div>
-    </div>
-</div>
-
-{% endblock %}

+ 0 - 100
flask/app/templates/testrun/index_old.html

@@ -1,100 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}
-    Testruns
-{% endblock %}
-
-{% block content %}
-<div class="container-fluid mt-5">
-<div class="row">
-    <nav class="col-md-2 d-none d-md-block bg-light sidebar">
-        <div class="sidebar-sticky">
-            <ul class="nav flex-column my-3">
-                <li class="nav-item">
-                    <a class="nav-link" data-toggle="collapse" href="#Testruns">
-                        Testruns                        </a>
-                    <div class="collapse" id="Testruns">
-                        <a class="btn btn-primary btn-sm ml-4" data-type="testrun" href="#">Create New Testrun</a>
-                        {% for item in items.testruns %}
-                            <a class="dropdown-item" data-type="testrun" data-id="{{ item.id }}" href="">{{ item }}</a>
-                        {% endfor %}
-                    </div>
-                </li>
-                <li class="nav-item">
-                    <a class="nav-link" data-toggle="collapse" href="#TestCaseSequences">
-                        Test Case Sequences
-                    </a>
-                    <div class="collapse" id="TestCaseSequences">
-                        <a class="btn btn-primary btn-sm ml-4" data-type="testcase_sequence" href="#">Create New Test Case Sequence</a>
-                        {% for item in items.testcase_sequances %}
-                            <a class="dropdown-item" data-type="testcase_sequence" data-id="{{ item.id }}" href="">{{ item }}</a>
-                        {% endfor %}
-                    </div>
-                </li>
-                <!-- DataFile excludded
-                <li class="nav-item">
-                    <a class="nav-link" data-toggle="collapse" href="#DataFiles">
-                        Data Files
-                    </a>
-                    <div class="collapse" id="DataFiles">
-                        {% for item in items.datafiles %}
-                            <a class="dropdown-item" data-type="datafile" data-id="{{ item.id }}" href="">{{ item }}</a>
-                        {% endfor %}
-                    </div>
-                </li>
-            -->
-                <li class="nav-item">
-                    <a class="nav-link" data-toggle="collapse" href="#TestCases">
-                        Test Cases
-                    <div class="collapse" id="TestCases">
-                        <a class="btn btn-primary btn-sm ml-4" data-type="testcase" href="#">Create New Test Case</a>
-                        {% for item in items.testcases %}
-                            <a class="dropdown-item" data-type="testcase" data-id="{{ item.id }}" href="">{{ item }}</a>
-                        {% endfor %}
-                    </div>
-                </li>
-                <li class="nav-item">
-                    <a class="nav-link" data-toggle="collapse" href="#TestStepSequences">
-                        Test Step Sequences
-                    </a>
-                    <div class="collapse" id="TestStepSequences">
-                        <a class="btn btn-primary btn-sm ml-4" data-type="teststep_sequence" href="#">Create New Test Step Sequence</a>
-                        {% for item in items.teststep_sequences %}
-                            <a class="dropdown-item" data-type="teststep_sequence" data-id="{{ item.id }}" href="">{{ item }}</a>
-                        {% endfor %}
-                    </div>
-                </li>
-                <li class="nav-item">
-                    <a class="nav-link" data-toggle="collapse" href="#TestSteps">
-                        Test Steps
-                    </a>
-                    <div class="collapse" id="TestSteps">
-                        <a class="btn btn-primary btn-sm ml-4" data-type="teststep" href="#">Create New Test Step</a>
-                        {% for item in items.teststeps %}
-                            <a class="dropdown-item" data-type="teststep" data-id="{{ item.id }}" href="">{{ item }}</a>
-                        {% endfor %}
-                    </div>
-                </li>
-            </ul>
-        </div>
-    </nav>
-
-    <main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
-
-        <h1 class="py-2">
-            {% with msg = get_flashed_messages() %}
-                {% if msg %}
-                    {{ msg|last }}
-                {% endif %}
-            {% endwith %}
-        </h1>
-        <h3 class="border-bottom pb-2">Please select an item.</h3> 
-
-    </main>
-</div>
-</div>
-
-<!-- Load local JS -->
-<script src="{{ url_for('static', filename='testrun.js') }}"></script>
-
-{% endblock %}

+ 0 - 162
flask/app/templates/testrun/item.html

@@ -1,162 +0,0 @@
-    <div class="border-bottom pt-3">
-        <h1 >{{ item }}</h1>
-        <p><small>
-                Created {{ item.created.strftime('%Y-%m-%d %H:%M') }} by {{ item.creator }}
-        </small></p>
-    
-        {% if item.edited %}
-        <p><small>
-            Last edidted {{ item.edited.strftime('%Y-%m-%d %H:%M') }} by {{ item.editor }}
-        </small></p>
-        {% endif %}
-    </div>
-
-    <div class="border-bottom pt-3">
-        <h4>Description:</h4>
-        <p>{{ item.description }}</div>
-    <div>
-
-    {% if type == "testrun" %}
-    <div class="border-bottom pt-3">
-        <h4>Test Case Sequences:</h4>
-        {% if item.testcase_sequences %}
-            <ul>
-            {% for sequence in item.testcase_sequences %}
-                <li>{{ sequence }}</li>
-            {% endfor %}
-            </ul>
-        {% else %}
-            <p>No Test Case Sequence Defined</p>
-        {% endif %}
-    </div>
-    {% endif %}
-
-    {% if type == "testcase_sequence" or type == "testcase" or type == "teststep_sequences" %}
-    <div class="border-bottom pt-3">
-        <h4>Class Name:</h4>
-        {{ item.classname }}
-    </div>
-    {% endif %}
-
-    {% if type == "testcase_sequence" %}
-    <div class="border-bottom pt-3">
-        <h4>Data Files:</h4>
-        {% if item.datafiles %}
-            <ul>
-            {% for datafile in item.datafiles %}
-                <li>{{ datafile }}</li>
-            {% endfor %}
-            </ul>
-        {% else %}
-            <p>No Data File Defined</p>
-        {% endif %}
-    </div>
-
-    <div class="border-bottom pt-3">
-        <h4>Test Cases:</h4>
-        {% if item.testcases %}
-            <ul>
-            {% for testcase in item.testcases %}
-                <li>{{ testcase }}</li>
-            {% endfor %}
-            </ul>
-        {% else %}
-            <p>No Test Case Defined</p>
-        {% endif %}
-    </div>
-    {% endif %}
-
-    {% if type == "testcase" %}
-    <div class="border-bottom pt-3">
-        <h4>Browser Type:</h4>
-        {{ item.browser_type }}
-    </div>
-
-    <div class="border-bottom pt-3">
-        <h4>Test Case Type:</h4>
-        {{ item.testcase_type }}
-    </div>
-
-    <div class="border-bottom pt-3">
-        <h4>Test Case Step Sequence:</h4>
-        {% if item.teststep_sequences %}
-            <ul>
-            {% for sequence in item.teststep_sequences %}
-                <li>{{ sequence }}</li>
-            {% endfor %}
-            </ul>
-        {% else %}
-            <p>No Test Case Step Sequence Defined</p>
-        {% endif %}
-    </div>
-    {% endif %}
-
-    {% if type == "teststep_sequence" %}
-    <div class="border-bottom pt-3">
-        <h4>Test Steps:</h4>
-        {% if item.teststeps %}
-            <ul>
-            {% for step in item.teststeps %}
-                <li>{{ step }}</li>
-            {% endfor %}
-            </ul>
-        {% else %}
-            <p>No Test Step Defined</p>
-        {% endif %}
-    </div>
-    {% endif %}
-
-    {% if type == "teststep" %}
-    <div class="border-bottom pt-3">
-        <h4>Activity Type:</h4>
-        {{ item.activity_type }}
-    </div>
-
-    <div class="border-bottom pt-3">
-        <h4>Locator Type:</h4>
-        {{ item.locator_type }}
-    </div>
-
-    <div class="border-bottom pt-3">
-        <h4>Locator:</h4>
-        {{ item.locator }}
-    </div>
-
-    <div class="border-bottom pt-3">
-        <h4>Optional:</h4>
-        {{ item.optional }}
-    </div>
-
-    <div class="border-bottom pt-3">
-        <h4>Timeout:</h4>
-        {{ item.timeout }}
-    </div>
-
-    <div class="border-bottom pt-3">
-        <h4>Release:</h4>
-        {{ item.release }}
-    </div>
-
-    <div class="border-bottom pt-3">
-        <h4>Value:</h4>
-        {{ item.value }}
-    </div>
-
-    <div class="border-bottom pt-3">
-        <h4>Value 2:</h4>
-        {{ item.value2 }}
-    </div>
-
-    <div class="border-bottom pt-3">
-        <h4>Comparision:</h4>
-        {{ item.comparision }}
-    </div>
-    {% endif %}
-
-    <p>
-        <button class="btn btn-primary px-5 my-3 mr-3" onclick="edit_item('{{ type }}', '{{ item.id }}')">Edit</button>
-        <button class="btn btn-danger px-5 my-3" onclick="delete_item('{{ type }}', '{{ item.name}}', '{{ item.id }}')">Delete</button>
-    </p>
-
-
-

+ 0 - 425
flask/app/templates/testrun/item_list.html

@@ -1,425 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}
-    {{ type|name_by_type }}
-{% endblock %}
-
-{% block content %}
-
-<main>
-
-<!-- bread crumb -->
-<nav aria-label="breadcrumb">
-    <ol class="breadcrumb">
-        <li class="breadcrumb-item"><a href="/">Home</a></li>
-        <li class="breadcrumb-item active" aria-current="page">{{ type|name_by_type }}</li>
-    </ol>
-</nav>
-
-<!-- flash messages -->
-{% with messages = get_flashed_messages(with_categories=true) %}
-    {% for category, msg in messages %}
-        <div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">
-            {{ msg }}
-            <button type="button" class="close" data-dismiss="alert" aria-label="Close">
-                <span aria-hidden="true">&times;</span>
-            </button>
-        </div>
-    {% endfor %}
-{% endwith %}
-
-<!-- item list -->
-<div class="container mt-5">
-
-    <!-- item title -->
-    <div class="row">
-        <div class="col-md-9">
-            <h2>{{ type|name_by_type }}</h2>
-        </div>
-        <!-- create button -->
-        <div class="col-md-3">
-            <a class="btn btn-primary {% if type == 'testrun' %} btn-right {% endif %}" href="/{{ type }}/new" role="button">
-                <strong>&plus; </strong>create {{ type|name_by_type(False) }}
-            </a>
-        </div>
-    </div>
-
-    <!-- filter box -->
-    <div class="row mb-3">
-        <div class="col-md-9">
-            <input type="text" id="filter" placeholder="Search..." onkeyup="filter_items(this)">
-        </div>
-        <!-- Testrun Import button -->
-        {% if type == 'testrun' %}
-        <div class="col-md-3">
-            <button type="button" class="btn btn-success btn-right" data-toggle="modal" data-target="#importModal">
-                <strong>&plus; </strong>import {{ type|name_by_type(False) }}
-            </button>
-        </div>
-
-        <!-- Import Modal -->
-        <div class="modal fade" id="importModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
-            <div class="modal-dialog" role="document">
-                <div class="modal-content">
-                    <div class="modal-header">
-                        <h5 class="modal-title" id="exampleModalLabel">Import {{ type|name_by_type(False) }}</h5>
-                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-                            <span aria-hidden="true">&times;</span>
-                        </button>
-                    </div>
-                    <form method="post" enctype="multipart/form-data" action="{{ url_for('import_testsun') }}">
-                        {{ form.hidden_tag() }}
-                        <div class="modal-body">
-                            <div class="custom-file">
-                                <!--input type="file" class="custom-file-input" id="customFile" onchange="get_file(this)"-->
-                                {{ form.file(class="custom-file-input", accept=".xlsx", onchange="get_file(this)") }}
-                                <label class="custom-file-label" for="{{ form.file.name }}">Choose a file</label>
-                            </div>
-                        </div>
-                        <div class="modal-footer">
-                            <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
-                            <button type="submit" class="btn btn-primary">Import</button>
-                        </div>
-                    </form>
-                </div>
-            </div>
-        </div>
-
-        {% endif %}
-    </div>
-
-    <!-- header -->
-    <div class="row border-top border-bottom py-3">
-        <div class="col-md-4"><strong>Name</strong></div>
-        <div class="col-md-5"><strong>Description</strong></div>
-
-        <!-- Test Step fields -->
-        {% if type == 'teststep' %}
-            <div class="col"><strong>Activity Type</strong></div>
-        {% else %}
-            <div class="col"><strong>Created</strong></div>
-        {% endif %}
-    </div>
-
-    <!-- item list -->
-    {% for item in items %}
-        <div class="row border-top py-3 testrun-item">
-            <div class="col-md-4 text-break">
-                <a class="filtered" data-toggle="collapse" href="#item{{ item.id }}" aria-expanded="false">
-                    {{ item.name }}
-                </a>
-            </div>
-            <div class="filtered col-md-5 text-break text-break">{{ item.description }}</div>
-            {% if type == 'teststep' %}    
-                <div class="filtered col">{{ item.activity_type }}</div>
-            {% else %}
-                <div class="col">{{ item.created|time }}</div>
-            {% endif %}
-
-            <div class="col-12 collapse mt-1" id="item{{ item.id }}">
-                <!-- created data -->
-                <p><small>
-                    Created {{ item.created|time }} by {{ item.creator }}
-                </small></p>
-                <!-- edited data -->
-                {% if item.edited %}
-                    <p><small>
-                        Last edidted {{ item.edited|time }} by {{ item.editor }}
-                    </small></p>
-                {% endif %}
-
-                <ul class="list-group">
-
-                    <!-- description -->
-                    {% if not type == 'teststep' %}
-                        <li class="list-group-item h5 list-group-item-dark">Description</li>
-                        <li class="list-group-item">{{ item.description }}</li>
-                    {% endif %}
-
-                    <!-- Testrun fields -->
-                    {% if type == 'testrun' %}
-
-                    <li class="list-group-item h5 list-group-item-dark">{{ 'testcase_sequence'|name_by_type }}</li>
-                    {% if item.testcase_sequences %}
-                        {% for sequence in item.testcase_sequences %}
-                        <li class="list-group-item">{{ sequence }}</li>
-                        {% endfor %}
-                    {% else %}
-                        <li class="list-group-item">No Test Case Sequence Defined</li>
-                    {% endif %}
-
-                    {% endif %}
-
-                    <!-- Class Name -->
-                    {% if type == 'testcase_sequence' or type == 'testcase' or type == 'teststep_sequences' %}
-
-                    <li class="list-group-item h5 list-group-item-dark">Class Name</li>
-                    <li class="list-group-item">{{ item.classname }}</li>
-                    
-                    {% endif %}
-
-                    <!-- Testcase Sequence fields -->
-                    {% if type == 'testcase_sequence' %}
-
-                    <li class="list-group-item h5 list-group-item-dark">Data Files</li>
-                    {% if item.datafiles %}
-                        {% for datafile in item.datafiles %}
-                        <li class="list-group-item">{{ datafile }}</li>
-                        {% endfor %}
-                    {% else %}
-                        <li class="list-group-item">No Data File Defined</li>
-                    {% endif %}
-
-                    <li class="list-group-item h5 list-group-item-dark">{{ 'testcase'|name_by_type }}</li>
-                    {% if item.testcases %}
-                        {% for testcase in item.testcases %}
-                        <li class="list-group-item">{{ testcase }}</li>
-                        {% endfor %}
-                    {% else %}
-                        <li class="list-group-item">No Test Case Defined</li>
-                    {% endif %}
-
-                    {% endif %}
-
-                    <!-- Test Case fields -->
-                    {% if type == 'testcase' %}
-
-                    <li class="list-group-item h5 list-group-item-dark">Browser Type</li>
-                    <li class="list-group-item">{{ item.browser_type }}</li>
-
-                    <li class="list-group-item h5 list-group-item-dark">Test Case Type</li>
-                    <li class="list-group-item">{{ item.testcase_type }}</li>
-
-                    <li class="list-group-item h5 list-group-item-dark">{{ 'teststep_sequence'|name_by_type }}</li>
-                    {% if item.teststep_sequences %}
-                        {% for sequence in item.teststep_sequences %}
-                        <li class="list-group-item">{{ sequence }}</li>
-                        {% endfor %}
-                    {% else %}
-                        <li class="list-group-item">No Test Case Step Sequence Defined</li>
-                    {% endif %}
-
-                    {% endif %}
-
-                    <!-- Test Step Sequence fields -->
-                    {% if type == 'teststep_sequence' %}
-
-                    <li class="list-group-item h5 list-group-item-dark">{{ 'teststep'|name_by_type }}</li>
-                    {% if item.teststeps %}
-                        {% for step in item.teststeps %}
-                        <li class="list-group-item">{{ step }}</li>
-                        {% endfor %}
-                    {% else %}
-                        <li class="list-group-item">No Test Step Defined</li>
-                    {% endif %}
-
-                    {% endif %}
-
-                    <!-- Test Step fields -->
-                    {% if type == 'teststep' %}
-                    <li class="list-group-item h5 list-group-item-dark">Parameters</li>
-                    <li class="list-group-item">
-                        <!-- 
-                        <div class="row border-bottom py-2">
-                            <div class="col-md-3">
-                                <strong>Activity Type</strong>
-                            </div>
-                            <div class="col">
-                                {{ item.activity_type }}
-                            </div>
-                        </div>
-                        -->
-
-                        <!-- Valu/Comparision -->
-                        <div class="row py-2">
-                            <div class="col-md-2">
-                                <strong>Value</strong>
-                            </div>
-                            <div class="col-md-2">
-                                <strong>Comparision</strong>
-                            </div>
-                            <div class="col-md-2">
-                                <strong>Value 2</strong>
-                            </div>
-                        </div>
-
-                        <div class="row border-bottom py-2">
-                            <div class="col-md-2 text-break">
-                                {{ item.value }}
-                            </div>
-                            <div class="col-md-2">
-                                {{ item.comparision }}
-                            </div>
-                            <div class="col-md-2">
-                                {{ item.value2 }}
-                            </div>
-                        </div>
-
-                        <!-- others -->
-                        <div class="row border-bottom py-2">
-                            <div class="col-md-3">
-                                <strong>Locator Type</strong>
-                            </div>
-                            <div class="col-md-9">
-                                {{ item.locator_type }}
-                            </div>
-                        </div>
-
-                        <div class="row border-bottom py-2">
-                            <div class="col-md-3">
-                                <strong>Locator</strong>
-                            </div>
-                            <div class="col-md-9">
-                                {% if item.locator %}{{ item.locator }}{% endif %}
-                            </div>
-                        </div>
-
-
-                        <div class="row border-bottom py-2">
-                            <div class="col-md-3">
-                                <strong>Optional</strong>
-                            </div>
-                            <div class="col-md-9">
-                                {{ item.optional }}
-                            </div>
-                        </div>
-
-                        <div class="row border-bottom py-2">
-                            <div class="col-md-3">
-                                <strong>Timeout</strong>
-                            </div>
-                            <div class="col-md-9">
-                                {% if item.timeout %}{{ item.timeout }}{% endif %}
-                            </div>
-                        </div>
-
-                        <div class="row border-bottom py-2">
-                            <div class="col-md-3">
-                                <strong>Release</strong>
-                            </div>
-                            <div class="col-md-9">
-                                {% if item.release %}{{ item.release }}{% endif %}
-                            </div>
-                        </div>
-                    </li>
-
-                    {% endif %}
-
-                </ul>
-                
-                <!-- buttons -->
-                <p>
-                    <a class="btn btn-primary px-5 my-3 mr-3" href="/{{ type }}/{{ item.id }}/edit" role="button">Edit</a>
-                    <!-- Testrun buttons -->
-                    {% if type == 'testrun' %}
-                        <!-- Update button -->
-                        <button type="button" class="btn btn-primary px-5 my-3 mr-3" data-toggle="modal" data-target="#updateModal" onmousedown="set_update_item('{{ item.id }}')">
-                            Update
-                        </button>
-                        <!-- Export button -->
-                        <button type="button" class="btn btn-success px-5 my-3 mr-3" data-toggle="modal" data-target="#exportModal" onmousedown="set_export_id('{{ item.id }}')">
-                            Export
-                        </button>
-                    {% endif %}
-                    <button class="btn btn-danger px-5 my-3" data-toggle="modal" data-target="#deleteModal" onmousedown="set_delete_item('{{ type|name_by_type }}','{{ type }}','{{ item.id }}')">
-                        Delete
-                    </button>
-                </p>
-               
-            </div>
-        </div>
-    {% endfor %}
-
-    <!-- Update Modal -->
-    {% if type == 'testrun' %}
-        <div class="modal fade" id="updateModal" tabindex="-1" role="dialog" aria-labelledby="updateModalLabel" aria-hidden="true">
-            <div class="modal-dialog" role="document">
-                <div class="modal-content">
-                    <div class="modal-header">
-                        <h5 class="modal-title" id="updateLabel"></h5>
-                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-                            <span aria-hidden="true">&times;</span>
-                        </button>
-                    </div>
-                    <form method="post" enctype="multipart/form-data" id="updateForm">
-                        {{ form.hidden_tag() }}
-                        <div class="modal-body">
-                            <div class="custom-file">
-                                {{ form.file(class="custom-file-input", accept=".xlsx", onchange="get_file(this)") }}
-                                <label class="custom-file-label" for="{{ form.file.name }}">Choose a file</label>
-                            </div>
-                        </div>
-                        <div class="modal-footer">
-                            <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
-                            <button type="submit" class="btn btn-primary">Update</button>
-                        </div>
-                    </form>
-                </div>
-            </div>
-        </div>
-    {% endif %}
-
-    <!-- Export  Modal -->
-    <div class="modal fade" id="exportModal" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="exportLabel" aria-hidden="true">
-        <div class="modal-dialog" role="document">
-            <div class="modal-content">
-                <div class="modal-header">
-                    <h5 class="modal-title" id="exportLabel">Export Testrun</h5>
-                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-                        <span aria-hidden="true">&times;</span>
-                    </button>
-                </div>
-                <div class="modal-body" id="exportModalBody">
-                    <div id="exportResponse">
-                    </div>
-                    <div id="exportRequest">
-                        <div class="custom-control custom-radio">
-                            <input type="radio" id="xlsxRadio" name="formatRadio" class="custom-control-input" checked>
-                            <label class="custom-control-label" for="xlsxRadio">XLSX</label>
-                        </div>
-                        <div class="custom-control custom-radio">
-                            <input type="radio" id="jsonRadio" name="formatRadio" class="custom-control-input" disabled>
-                            <label class="custom-control-label" for="jsonRadio">JSON</label>
-                        </div>
-                    </div>
-                </div>
-                <div class="modal-footer">
-                    <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
-                    <button type="button" class="btn btn-primary" id="exportButton" onclick="exportTestrun(this)">Export</button>
-                </div>
-            </div>
-        </div>
-    </div>
-
-    <!-- Delete Modal -->
-    <div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="deleteLabel" aria-hidden="true">
-        <div class="modal-dialog" role="document">
-            <div class="modal-content">
-                <div class="modal-header">
-                    <h5 class="modal-title" id="deleteLabel"></h5>
-                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-                        <span aria-hidden="true">&times;</span>
-                    </button>
-                </div>
-                <div class="modal-body">
-                    Delete all child elements?
-                </div>
-                <div class="modal-footer dialog" id="deleteButtons">
-                    <button type="button" class="btn btn-secondary btn-right" onclick="delete_item(this.parentElement, true)">
-                        Yes
-                    </button>
-                    <button type="button" class="btn btn-primary btn-right" onclick="delete_item(this.parentElement, false)">
-                        No
-                    </button>
-                </div>
-            </div>
-        </div>
-    </div>
-
-
-
-</div>
-</main>
-
-
-{% endblock %}

+ 0 - 38
flask/app/templates/testrun/login.html

@@ -1,38 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}
-Login
-{% endblock %}
-
-{% block content %}
-<div class="d-flex flex-column text-center mt-5">
-	<h1 class="py-5">Please Login</h1>
-	
-	<!-- login form -->
-	<form method="post">
-		{{ form.hidden_tag() }}
-
-		<p>
-			{% for error in form.username.errors %}
-				<p class="text-danger">{{ error }}</p>
-			{% endfor %}
-			{{ form.username(placeholder="Username") }}
-		</p>
-		<p>
-			{% for error in form.password.errors %}
-				<p class="text-danger">{{ error }}</p>
-			{% endfor %}
-			{{ form.password(placeholder="Password") }}
-		</p>
-		<p>
-			<button class="btn btn-primary px-5" type="submit">Login</button>
-		</p>
-	</form>
-
-	<div class="mt-2">
-		Not registered yet? <a href="/signup">Sign up here</a>
-	</div>
-
-</div>
-
-{% endblock %}

+ 0 - 43
flask/app/templates/testrun/signup.html

@@ -1,43 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}
-Sign up
-{% endblock %}
-
-{% block content %}
-<div class="d-flex flex-column text-center mt-5">
-	<h1 class="py-5">Register a new user</h1>
-	
-	<!-- sign up form -->
-	<form method="post">
-		{{ form.hidden_tag() }}
-		<p>
-			{% for error in form.username.errors %}
-				<p class="text-danger">{{ error }}</p>
-			{% endfor %}
-			{{ form.username(placeholder="Username") }}
-		</p>
-		<p>
-			{% for error in form.password.errors %}
-				<p class="text-danger">{{ error }}</p>
-			{% endfor %}
-			{{ form.password(placeholder="Password") }}
-		</p>
-		<p>
-			{% for error in form.password2.errors %}
-				<p class="text-danger">{{ error }}</p>
-			{% endfor %}
-			{{ form.password2(placeholder="Password again") }}
-		</p>
-		<p>
-			<button class="btn btn-primary px-5" type="submit">Sign up</button>
-		</p>
-	</form>
-
-	<div class="mt-2">
-		Already registered? <a href="/login">Log in here</a>
-	</div>
-
-</div>
-
-{% endblock %}

+ 0 - 836
flask/app/utils.py

@@ -1,836 +0,0 @@
-import os
-from app import models, db, app
-from datetime import datetime
-import xlsxwriter, xlrd, json
-from flask_login import current_user
-
-#
-# Default VALUES
-#
-CLASSNAME_TESTCASESEQUENCE = 'GC.CLASSES_TESTCASESEQUENCE'
-CLASSNAME_TESTCASE = 'GC.CLASSES_TESTCASE'
-CLASSNAME_TESTSTEP = 'GC.CLASSES_TESTSTEPMASTER'
-
-BROWSER_TYPE = 'GC.BROWSER_FIREFOX'
-TESTCASE_TYPE = 'Browser'
-
-#
-# item categories
-#
-def getItemCategories():
-	categories = {}
-	categories['main'] = [
-		'testrun',
-		'testcase_sequence',
-		'testcase',
-		'teststep_sequence',
-		'teststep',
-	]
-
-	return categories
-
-#
-# get name of the item_type
-#
-def getItemType(item_type, plural=False):	
-	# main items 
-	if item_type == 'testrun':
-		name = 'Testrun'
-	elif item_type == 'testcase_sequence':
-		name = 'Test Case Sequence'
-	elif item_type == 'testcase':
-		name = 'Test Case'
-	elif item_type == 'teststep_sequence':
-		name = 'Test Step Sequence'
-	elif item_type == 'teststep':
-		name = 'Test Step'
-	else:
-		# wrong item_type
-		return ''
-
-	# check for plurals
-	if plural:
-		name += 's'
-
-	return name
-
-#
-# generate choices of items
-#
-
-def getTestCaseSequences():
-	choices = []
-	for item in models.TestCaseSequence.query.all():
-		choices.append((f'{item.id}', item.name))
-	return choices
-
-def getDataFiles():
-	choices = []
-	for item in models.DataFile.query.all():
-		choices.append((f'{item.id}', item.filename))
-	return choices
-
-def getTestCases():
-	choices = []
-	for item in models.TestCase.query.all():
-		choices.append((f'{item.id}', item.name))
-	return choices
-
-def getTestStepSequences():
-	choices = []
-	for item in models.TestStepSequence.query.all():
-		choices.append((f'{item.id}', item.name))
-	return choices
-
-def getTestSteps():
-	choices = []
-	for item in models.TestStepExecution.query.all():
-		choices.append((f'{item.id}', item.name))
-	return choices
-
-def getClassNames():
-	choices = []
-	for item in models.ClassName.query.all():
-		choices.append((f'{item.id}', item.name))
-	return choices
-
-def getBrowserTypes():
-	choices = []
-	for item in models.BrowserType.query.all():
-		choices.append((f'{item.id}', item.name))
-	return choices
-
-def getTestCaseTypes():
-	choices = []
-	for item in models.TestCaseType.query.all():
-		choices.append((f'{item.id}', item.name))
-	return choices
-
-def getActivityTypes():
-	choices = []
-	for item in models.ActivityType.query.all():
-		choices.append((f'{item.id}', item.name))
-	return choices
-
-def getLocatorTypes():
-	choices = []
-	for item in models.LocatorType.query.all():
-		choices.append((f'{item.id}', item.name))
-	return choices
-
-
-#
-# Comaprisions
-#
-
-COMPARISIONS = [
-	'=',
-	'>',
-	'<',
-	'>=',
-	'<=',
-	'<>',
-]
-
-def getComparisionChoices():
-	return [('0', 'none')] + [(f'{i+1}', COMPARISIONS[i]) for i in range(len(COMPARISIONS))]
-
-def getComparisionId(option):
-	for i in range(len(COMPARISIONS)):
-		if option == COMPARISIONS[i]:
-			return f'{i+1}'
-	return '0'
-
-#
-# Get Items By Name
-#
-
-def getOrCreateClassNameByName(name, description):
-	# get ClassName from DB
-	classname = models.ClassName.query.filter_by(name=name).first()
-	if classname is None:
-		# create ClassName if it doesn't exist
-		classname = models.ClassName(
-			name=name,
-			description=description,
-		)
-		db.session.add(classname)
-		db.session.commit()
-		app.logger.info(f'Created ClassName ID #{classname.id} by {current_user}.')
-
-	return classname 
-
-
-def getBrowserTypeByName(name):
-	# browser mapper
-	bm = {
-		'BROWSER_FIREFOX': "FF",
-		'BROWSER_CHROME': "Chrome",
-		'BROWSER_SAFARI': "Safari",
-		'BROWSER_EDGE': "Edge",
-	}
-	return models.BrowserType.query.filter_by(name=bm[name.split('.')[-1]]).first()
-
-def getTestCaseTypeByName(name):
-	return models.TestCaseType.query.filter_by(name=name).first()
-
-def getActivityTypeByName(name):
-	return models.ActivityType.query.filter_by(name=name).first()
-
-def getLocatorTypeByName(name):
-	if name:
-		return models.LocatorType.query.filter_by(name=name).first()
-	else:
-		return None
-
-def getBooleanValue(value):
-	if value:
-		return True
-	else:
-		return False
-
-#
-# Cascade Delete
-#
-
-def deleteCascade(item_type, item_id, ):
-	#
-	# implementation of cascade delete of items
-	#
-
-	# delete Testrun and its children
-	if item_type == 'testrun':
-		item = models.Testrun.query.get(item_id)
-		# delete children TestCaseSequences
-		for child in item.testcase_sequences:
-			deleteCascade('testcase_sequence', child.id)
-		# delete Testrun
-		db.session.delete(item)
-		db.session.commit()
-		app.logger.info(f'Deleted {item_type} id {item_id} by {current_user}.')
-
-	# delete TestCaseSequence and its children
-	elif item_type == 'testcase_sequence':
-		item = models.TestCaseSequence.query.get(item_id)
-		# check if item has not more then one parent
-		if len(item.testrun) <= 1:
-			# delete children TestCases
-			for child in item.testcases:
-				deleteCascade('testcase', child.id)
-			# delete TestCaseSequence
-			db.session.delete(item)
-			db.session.commit()
-			app.logger.info(f'Deleted {item_type} id {item_id} by {current_user}.')
-
-	# delete TestCase and its children
-	elif item_type == 'testcase':
-		item = models.TestCase.query.get(item_id)
-		# check if item has not more then one parent
-		if len(item.testcase_sequence) <= 1:
-			# delete children TestCaseSequences
-			for child in item.teststep_sequences:
-				deleteCascade('teststep_sequence', child.id)
-			# delete TestCase
-			db.session.delete(item)
-			db.session.commit()
-			app.logger.info(f'Deleted {item_type} id {item_id} by {current_user}.')
-
-	# delete TestCaseSequence and its children
-	elif item_type == 'teststep_sequence':
-		item = models.TestStepSequence.query.get(item_id)
-		# check if item has not more then one parent
-		if len(item.testcase) <= 1:
-			# delete children TestStepExecutions
-			for child in item.teststeps:
-				deleteCascade('teststep', child.id)
-			# delete TestCaseSequence
-			db.session.delete(item)
-			db.session.commit()
-			app.logger.info(f'Deleted {item_type} id {item_id} by {current_user}.')
-
-	# delete TestStepExecution
-	elif item_type == 'teststep':
-		item = models.TestStepExecution.query.get(item_id)
-		db.session.delete(item)
-		db.session.commit()
-		app.logger.info(f'Deleted {item_type} id {item_id} by {current_user}.')
-
-	# invalid type
-	else:
-		raise Exception(f'Item type {item_type} does not exists.')
-
-
-#
-# Testrun export/import
-#
-
-def exportXLSX(testrun_id):
-	#
-	# Exports Testrun to XLSX
-	#
-
-	# get testrun
-	testrun = models.Testrun.query.get(testrun_id)
-	testrun_json = testrun.to_json()
-
-	# create workbook
-	headers = {
-		'TestRun': [
-			'Attribute',
-			'Value',
-		],
-
-		'TestCaseSequence': [
-			'Number',
-			'SequenceClass',
-			'TestDataFileName',
-		],
-		
-		'TestCase': [
-			'TestCaseSequenceNumber',
-			'TestCaseNumber',
-			'TestCaseClass'
-			'TestCaseType',
-			'Browser',
-		],
-		
-		'TestStep': [
-			'TestCaseSequenceNumber',
-			'TestCaseNumber',
-			'TestStepNumber',
-			'TestStepClass',
-		],
-		
-		'TestStepExecution': [
-			'TestCaseSequenceNumber',
-			'TestCaseNumber',
-			'TestStepNumber',
-			'TestStepExecutionNumber',
-			'Activity',
-			'LocatorType',
-			'Locator',
-			'Value',
-			'Comparison',
-			'Value2',
-			'Timeout',
-			'Optional',
-			'Release',
-		],
-	}
-
-	xlsx_file = f'Testrun_{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}.xlsx'
-	workbook = xlsxwriter.Workbook(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'static/files', xlsx_file))
-	worksheets = {}
-	for sheet in headers:
-		worksheets[sheet] = workbook.add_worksheet(sheet)
-
-	# write headers
-	for key, items in headers.items():
-		for col in range(len(items)):
-			worksheets[key].write(0, col, items[col])
-
-	# write data
-	# to TestRun
-	worksheets['TestRun'].write(1, 0, 'Export Format')
-	worksheets['TestRun'].write(1, 1, 'XLSX')
-
-	# to TestCaseSequence
-	i = 1 # i for TestCaseSequence Number
-	for testcase_sequence in testrun.testcase_sequences: 
-		worksheets['TestCaseSequence'].write(i, 0, i)
-		worksheets['TestCaseSequence'].write(i, 1, testcase_sequence.classname.name)
-		worksheets['TestCaseSequence'].write(i, 2, testcase_sequence.datafiles[0].filename)
-
-		# to TestCase
-		j = 1 # j for TestCase 
-		for testcase in testcase_sequence.testcases:
-			worksheets['TestCase'].write(j, 0, i)
-			worksheets['TestCase'].write(j, 1, j)
-			worksheets['TestCase'].write(j, 2, testcase.classname.name)
-			worksheets['TestCase'].write(j, 3, testcase.testcase_type.name)
-			worksheets['TestCase'].write(j, 4, testcase.browser_type.name)
-
-			# to TestStep
-			k = 1 # k for TestStep
-			for teststep_sequence in testcase.teststep_sequences:
-				worksheets['TestStep'].write(k, 0, i)
-				worksheets['TestStep'].write(k, 1, j)
-				worksheets['TestStep'].write(k, 2, k)
-				worksheets['TestStep'].write(k, 3, teststep_sequence.classname.name)
-
-				# to TestStepExecution
-				m = 1 # m for TestStepExecution
-				for teststep in teststep_sequence.teststeps:
-					worksheets['TestStepExecution'].write(m, 0, i)
-					worksheets['TestStepExecution'].write(m, 1, j)
-					worksheets['TestStepExecution'].write(m, 2, k)
-					worksheets['TestStepExecution'].write(m, 3, m)
-					worksheets['TestStepExecution'].write(m, 4, teststep.activity_type.name)
-					worksheets['TestStepExecution'].write(m, 5, teststep.locator_type.name)
-					worksheets['TestStepExecution'].write(m, 6, teststep.locator)
-					worksheets['TestStepExecution'].write(m, 7, teststep.value)
-					worksheets['TestStepExecution'].write(m, 8, teststep.comparision)
-					worksheets['TestStepExecution'].write(m, 9, teststep.value2)
-					worksheets['TestStepExecution'].write(m, 10, teststep.timeout)
-					worksheets['TestStepExecution'].write(m, 11, teststep.optional)
-					worksheets['TestStepExecution'].write(m, 12, teststep.release)
-
-					m += 1
-				k += 1
-			j += 1
-		i += 1
-
-	workbook.close()
-	return xlsx_file
-
-
-def importXLSX(xlsx_file, item_id=None):
-	#
-	# imports testrun from xlsx file
-	#
-
-	if item_id is None:
-		app.logger.info(f'Importing a Testrun from {xlsx_file.filename} by {current_user}.')
-	else:
-		# get item
-		testrun = models.Testrun.query.get(item_id)
-		if testrun is None: # item does not exist
-			raise Exception(f'Testrun ID #{item_id} does not exists.')
-		app.logger.info(f'Updating Testrun ID #{item_id} from {xlsx_file.filename} by {current_user}.')
-	
-	# open xlsx
-	try:
-		xl = xlrd.open_workbook(file_contents=xlsx_file.read())
-	except xlrd.XLRDError:
-		raise Exception(f'File "{xlsx_file.filename}" could not be imporeted.')
-	# get file name
-	file_name = os.path.basename(xlsx_file.filename)
-
-	# Testrun object
-	if item_id is None:
-		# create Testrun
-		testrun  = models.Testrun(
-			name=file_name,
-			description=f'Imported from "{file_name}".',
-			creator=current_user,
-		)
-		db.session.add(testrun)
-		db.session.commit()
-		app.logger.info(f'Created Testrun ID #{testrun.id} by {current_user}.')
-	else:
-		# update Testrun
-		testrun.description = f'Updated from "{file_name}".'
-		testrun.editor = current_user
-		testrun.edited = datetime.utcnow()
-		db.session.commit()
-		app.logger.info(f'Updated Testrun ID #{item_id} by {current_user}.')
-
-	# TestCaseSequence objects
-	if item_id is None:
-		testcase_sequences = {}
-	else:
-		testcase_sequences = {i+1: testrun.testcase_sequences[i] for i in range(len(testrun.testcase_sequences))}
-	if 'TestCaseSequence' in xl.sheet_names():
-		# get sheet
-		testcase_sequence_sheet = xl.sheet_by_name('TestCaseSequence')
-		# get headers as dict
-		headers = {h[1]: h[0] for h in enumerate(testcase_sequence_sheet.row_values(0))}
-		# get TestCaseSequences
-		for row in range(1, testcase_sequence_sheet.nrows):
-			if headers.get('Number') is None:
-				# default number is 1
-				n = 1
-			else:
-				# get the number from sheet
-				n = int(testcase_sequence_sheet.cell(row, headers['Number']).value)
-			
-			# ClassName
-			if headers.get('SequenceClass') is None:
-				# default ClassName name
-				name = CLASSNAME_TESTCASESEQUENCE
-			else:
-				# get ClassName name from sheet
-				name = testcase_sequence_sheet.cell(row, headers['SequenceClass']).value
-			# get ClassName from DB or create if it doesn't exist
-			classname = getOrCreateClassNameByName(name, f'Imported from "{file_name}".')
-						
-			# DataFile
-			if headers.get('TestDataFileName') is None:
-				# default DataFile name
-				name = file_name
-			else:
-				# get DataFile name from sheet
-				name = testcase_sequence_sheet.cell(row, headers['TestDataFileName']).value
-			# get DataFile from DB
-			datafile = models.DataFile.query.filter_by(filename=name).first()
-			if datafile is None:
-				# create DataFile if it doesn't exist
-				datafile  = models.DataFile(
-					filename=name,
-					creator=current_user,
-				)
-				db.session.add(datafile)
-				db.session.commit()
-				app.logger.info(f'Created DataFile ID #{datafile.id} by {current_user}.')
-			
-			# TestCaseSequence
-			if n in testcase_sequences:
-				# update item
-				testcase_sequences[n].description = f'Updated from "{file_name}".'
-				testcase_sequences[n].editor = current_user
-				testcase_sequences[n].edited = datetime.utcnow()
-				testcase_sequences[n].classname = classname
-				testcase_sequences[n].datafiles = [datafile]
-				db.session.commit()
-				app.logger.info(f'Updated TestCaseSequence ID #{testcase_sequences[n].id} by {current_user}.')
-			else:
-				# create item
-				testcase_sequences[n] = models.TestCaseSequence(
-					name=f'{file_name}_{row}',
-					description=f'Imported from "{file_name}"',
-					creator=current_user,
-					classname=classname,
-					datafiles=[datafile],
-					testrun=[testrun],
-				)
-				db.session.add(testcase_sequences[n])
-				db.session.commit()
-				app.logger.info(f'Created TestCaseSequence ID #{testcase_sequences[n].id} by {current_user}.')
-			
-	else:
-		# create default TestCaseSequence
-		
-		# ClassName
-		# get ClassName from DB or create if it doesn't exist
-		classname = getOrCreateClassNameByName(CLASSNAME_TESTCASESEQUENCE, 'Default for TestCaseSequence.')
-		
-		# DataFile
-		# get DataFile from DB
-		datafile = models.DataFile.query.filter_by(filename=file_name).first()
-		if datafile is None:
-			# create DataFile if it doesn't exist
-			datafile  = models.DataFile(
-				filename=file_name,
-				creator=current_user,
-			)
-			db.session.add(datafile)
-			db.session.commit()
-			app.logger.info(f'Created DataFile ID #{datafile.id} by {current_user}.')
-
-		# TestCaseSequence
-		if 1 in testcase_sequences:
-			# update item
-			testcase_sequences[1].description = f'Updated to default.'
-			testcase_sequences[1].editor = current_user
-			testcase_sequences[1].edited = datetime.utcnow()
-			testcase_sequences[1].classname = classname
-			testcase_sequences[1].datafiles = [datafile]
-			db.session.commit()
-			app.logger.info(f'Updated TestCaseSequence ID #{testcase_sequences[1].id} by {current_user}.')
-		else:
-			# create item
-			testcase_sequences[1] = models.TestCaseSequence(
-				name=f'{file_name}',
-				description=f'Default for "{file_name}"',
-				creator=current_user,
-				classname=classname,
-				datafiles=[datafile],
-				testrun=[testrun],
-			)
-			db.session.add(testcase_sequences[1])
-			db.session.commit()
-			app.logger.info(f'Created TestCaseSequence ID #{testcase_sequences[1].id} by {current_user}.')
-
-	# TestCase objects
-	if item_id is None:
-		testcases = {i+1: {} for i in range(len(testcase_sequences))}
-	else:
-		testcases = {index: {j+1: item.testcases[j] for j in range(len(item.testcases))} for index, item in testcase_sequences.items()}
-
-	if 'TestCase' in xl.sheet_names():
-		# get sheet
-		testcase_sheet = xl.sheet_by_name('TestCase')
-		# get headers as dict
-		headers = {h[1]: h[0] for h in enumerate(testcase_sheet.row_values(0))}
-		# get TestCases
-		for row in range(1, testcase_sheet.nrows):
-			# get TestCaseSequenceNumber
-			if headers.get('TestCaseSequenceNumber') is None:
-				# default number is 1
-				i = 1
-			else:
-				# get the number from sheet
-				i = int(testcase_sheet.cell(row, headers['TestCaseSequenceNumber']).value)
-
-			# get TestCaseNumber
-			if headers.get('TestCaseNumber') is None:
-				# default number is 1
-				n = 1
-			else:
-				# get the number from sheet
-				n = int(testcase_sheet.cell(row, headers['TestCaseNumber']).value)
-			
-			# ClassName
-			if headers.get('TestCaseClass') is None:
-				# default ClassName name
-				name = CLASSNAME_TESTCASE
-			else:
-				# get ClassName name from sheet
-				name = testcase_sheet.cell(row, headers['TestCaseClass']).value
-			# get ClassName from DB or create if it doesn't exist
-			classname = getOrCreateClassNameByName(name, f'Imported from "{file_name}".')
-
-			# TestCase
-			# Browser Type
-			if headers.get('Browser') is None:
-				raise Exception('Sheet "TestCase" does not contain "Browser" column.')
-			else:
-				name = testcase_sheet.cell(row, headers['Browser']).value
-				browser_type = getBrowserTypeByName(name)
-				if browser_type is None:
-					raise Exception(f'Unknown browser type "{name}": sheet "TestCase", row {row+1}.')
-			# TestCase Type
-			if headers.get('TestCaseType') is None:
-				raise Exception('Sheet "TestCase" does not contain "TestCaseType" column.')
-			else:
-				name = testcase_sheet.cell(row, headers['TestCaseType']).value
-				testcase_type=getTestCaseTypeByName(name)
-				if testcase_type is None:
-					raise Exception(f'Unknown testcase type "{name}": sheet "TestCase" row {row+1}.')
-
-			if n in testcases[i]:
-				# update item
-				testcases[i][n].description = f'Updated from "{file_name}".'
-				testcases[i][n].editor = current_user
-				testcases[i][n].edited = datetime.utcnow()
-				testcases[i][n].classname = classname
-				testcases[i][n].browser_type = browser_type
-				testcases[i][n].testcase_type = testcase_type
-				db.session.commit()
-				app.logger.info(f'Updated TestCase ID #{testcases[n].id} by {current_user}.')
-			else:
-				# create item
-				testcases[i][n]  = models.TestCase(
-					name=f'{file_name}_{row}',
-					description=f'Imported from "{file_name}".',
-					creator=current_user,
-					classname=classname,
-					browser_type=browser_type,
-					testcase_type=testcase_type,
-					testcase_sequence=[testcase_sequences[i]],
-				)
-				db.session.add(testcases[i][n])
-				db.session.commit()
-				app.logger.info(f'Created TestCase ID #{testcases[i][n].id} by {current_user}.')
-	else:
-		# create default TestCase
-
-		# ClassName
-		# get ClassName from DB or create if it doesn't exist
-		classname = getOrCreateClassNameByName(CLASSNAME_TESTCASE, 'Default for TestCase.')
-
-		# TestCase
-		if 1 in testcases[1]:
-			# update item
-			testcases[1][1].description = f'Updated to default.'
-			testcases[1][1].editor = current_user
-			testcases[1][1].edited = datetime.utcnow()
-			testcases[1][1].classname = classname
-			testcases[1][1].browser_type = getBrowserTypeByName(BROWSER_TYPE)
-			testcases[1][1].testcase_type = getTestCaseTypeByName(TESTCASE_TYPE)
-			db.session.commit()
-			app.logger.info(f'Updated TestCase ID #{testcases[1][1].id} by {current_user}.')
-		else:
-			# create item
-			testcases[1][1]  = models.TestCase(
-				name=f'{file_name}',
-				description=f'Default for "{file_name}".',
-				creator=current_user,
-				classname=classname,
-				browser_type=getBrowserTypeByName(BROWSER_TYPE),
-				testcase_type=getTestCaseTypeByName(TESTCASE_TYPE),
-				testcase_sequence=[testcase_sequences[1]],
-			)
-			db.session.add(testcases[1][1])
-			db.session.commit()
-			app.logger.info(f'Created TestCase ID #{testcases[1][1].id} by {current_user}.')
-
-
-	# create TestSteps
-	teststeps = {}
-	if 'TestStep' in xl.sheet_names():
-		# get sheet
-		teststep_sheet = xl.sheet_by_name('TestStep')
-		# get headers as dict
-		headers = {h[1]: h[0] for h in enumerate(teststep_sheet.row_values(0))}
-		# get TestSteps
-		for row in range(1, teststep_sheet.nrows):
-			# get TestStepNumber
-			if headers.get('TestStepNumber') is None:
-				# default number is 1
-				n = 1
-			else:
-				# get the number from sheet
-				n = int(teststep_sheet.cell(row, headers['TestStepNumber']).value)
-
-			# get TestCaseNumber
-			if headers.get('TestCaseNumber') is None:
-				# default number is 1
-				j = 1
-			else:
-				# get the number from sheet
-				j = int(teststep_sheet.cell(row, headers['TestCaseNumber']).value)
-
-			# get TestCaseSequenceNumber
-			if headers.get('TestCaseSequenceNumber') is None:
-				# default number is 1
-				i = 1
-			else:
-				# get the number from sheet
-				i = int(teststep_sheet.cell(row, headers['TestCaseSequenceNumber']).value)
-
-			# ClassName
-			if headers.get('TestStepClass') is None:
-				# default ClassName name
-				name = CLASSNAME_TESTSTEP
-			else:
-				# get ClassName name from sheet
-				name = teststep_sheet.cell(row, headers['TestStepClass']).value
-			# get ClassName from DB or create if it doesn't exist
-			classname = getOrCreateClassNameByName(name, f'Imported from "{file_name}".')
-			
-			# TestCase
-			# ---------------------------------------------------------------> continue with update
-			teststeps[n]  = models.TestStepSequence(
-				name=f'{file_name}_{row}',
-				description=f'Imported from "{file_name}"',
-				creator=current_user,
-				classname=classname,
-				testcase=[testcases[i][j]],
-			)
-			db.session.add(teststeps[n])
-			db.session.commit()
-			app.logger.info(f'Created TestStepSequence id {teststeps[n].id} by {current_user}.')
-	else:
-		# create default TestStep
-		# ClassName
-		# get ClassName from DB or create if it doesn't exist
-		classname = getOrCreateClassNameByName(CLASSNAME_TESTSTEP, 'Default for TestStep')
-		
-		# TestStep
-		teststeps[1]  = models.TestStepSequence(
-			name=f'{file_name}_1',
-			description=f'Default for "{file_name}"',
-			creator=current_user,
-			classname=classname,
-			testcase=[testcases[1][1]]
-		)
-		db.session.add(teststeps[1])
-		db.session.commit()
-		app.logger.info(f'Created TestStepSequence id {teststeps[1].id} by {current_user}.')
-
-	# create TestStepsExecutions
-	if 'TestStepExecution' in xl.sheet_names():
-		# get sheet
-		teststep_execution_sheet = xl.sheet_by_name('TestStepExecution')
-		# get headers as dict
-		headers = {h[1]: h[0] for h in enumerate(teststep_execution_sheet.row_values(0))}
-		# get TestStepExecutions
-		for row in range(1, teststep_execution_sheet.nrows):
-			# get TestStepExecutionNumber
-			if headers.get('TestStepExecutionNumber') is None:
-				# default number is 1
-				n = row
-			else:
-				# get the number from sheet
-				n = int(teststep_execution_sheet.cell(row, headers['TestStepExecutionNumber']).value)
-			# get TestStepNumber
-			if headers.get('TestStepNumber') is None:
-				# default number is 1
-				teststep_n = 1
-			else:
-				# get the number from sheet
-				teststep_n = int(teststep_execution_sheet.cell(row, headers['TestStepNumber']).value)
-			# Activity Type
-			if headers.get('Activity') is None:
-				raise Exception('Sheet "TestStepExecution" does not contain "Activity" column.')
-			else:
-				name = teststep_execution_sheet.cell(row, headers['Activity']).value
-				activity_type = getActivityTypeByName(name.upper())
-				if activity_type is None:
-					raise Exception(f'Unknown activity type "{name}": sheet "TestStepExecution", row {row+1}')
-			# Locator Type
-			if headers.get('LocatorType') is None:
-				raise Exception('Sheet "TestStepExecution" does not contain "LocatorType" column.')
-			else:
-				locator_type = getLocatorTypeByName(teststep_execution_sheet.cell(row, headers['LocatorType']).value)
-			# get Locator
-			if headers.get('Locator') is None:
-				locator = None
-			else:
-				locator = teststep_execution_sheet.cell(row, headers['Locator']).value or None
-			# get Value
-			if headers.get('Value') is None:
-				value = None
-			else:
-				value = teststep_execution_sheet.cell(row, headers['Value']).value or None
-			# get Value 2
-			if headers.get('Value2') is None:
-				value2 = None
-			else:
-				value2 = teststep_execution_sheet.cell(row, headers['Value2']).value or None
-			# get Comparison
-			if headers.get('Comparison') is None:
-				comparision = None
-			else:
-				comparision = teststep_execution_sheet.cell(row, headers['Comparison']).value or None
-			# get Timeout
-			if headers.get('Timeout') is None:
-				timeout = None
-			else:
-				timeout = teststep_execution_sheet.cell(row, headers['Timeout']).value or None
-			# get Optional
-			if headers.get('Optional') is None:
-				optional = False
-			else:
-				optional = getBooleanValue(teststep_execution_sheet.cell(row, headers['Optional']).value)
-			# get Release
-			if headers.get('Release') is None:
-				release = None
-			else:
-				release = teststep_execution_sheet.cell(row, headers['Release']).value or None
-
-			# TestStepExecution
-			teststepex  = models.TestStepExecution(
-				name=f'{file_name}_{row}',
-				description=f'Imported from "{file_name}"',
-				creator=current_user,
-				teststep_sequence=teststeps[teststep_n],
-				activity_type=activity_type,
-				locator_type=locator_type,
-				locator=locator,
-				value=value,
-				comparision=comparision,
-				value2=value2,
-				timeout=timeout,
-				optional=optional,
-				release=release,
-			)
-			db.session.add(teststepex)
-			db.session.commit()
-			app.logger.info(f'Created TestStepExecution id {teststepex.id} by {current_user}.')
-
-	return 1
-
-
-
-
-
-
-
-
-
-
-
-
-

+ 0 - 450
flask/app/views.py

@@ -1,450 +0,0 @@
-
-from flask import render_template, redirect, flash, request, url_for, send_from_directory
-from flask_login import login_required, current_user, login_user, logout_user
-from flask.logging import default_handler
-from app import app, db, models, forms, utils
-from datetime import datetime
-
-# handle favicon requests
-@app.route('/favicon.ico') 
-def favicon(): 
-    return send_from_directory('static/media', 'favicon.ico', mimetype='image/vnd.microsoft.icon')
-
-@app.route('/')
-@login_required
-def index():
-	return render_template('testrun/index.html', items=utils.getItemCategories())
-
-@app.route('/<string:item_type>')
-@login_required
-def item_list(item_type):
-	# placeholder for import form
-	form = None
-	# get item list by type
-	if item_type == 'testrun':
-		items = models.Testrun.query.all()
-		# build form for importing a testrun
-		form = forms.TestrunImportForm()
-		
-	elif item_type == 'testcase_sequence':
-		items = models.TestCaseSequence.query.all()
-	elif item_type == 'testcase':
-		items = models.TestCase.query.all()
-	elif item_type == 'teststep_sequence':
-		items = models.TestStepSequence.query.all()
-	elif item_type == 'teststep':
-		items = models.TestStepExecution.query.all()
-	else:
-		app.logger.warning(f'Item type "{item_type}" does not exist. Requested by "{current_user}".')
-		flash(f'Item type "{item_type}" does not exist.', 'warning')
-		return redirect(url_for('index'))
-
-	return render_template('testrun/item_list.html', type=item_type, items=items, form=form)
-
-
-@app.route('/<string:item_type>/<int:item_id>', methods=['GET', 'POST'])
-@login_required
-def get_item(item_type, item_id):
-	# get item by type and id
-	if item_type == 'testrun':
-		item = models.Testrun.query.get(item_id)
-	elif item_type == 'testcase_sequence':
-		item = models.TestCaseSequence.query.get(item_id)
-	elif item_type == 'testcase':
-		item = models.TestCase.query.get(item_id)
-	elif item_type == 'teststep_sequence':
-		item = models.TestStepSequence.query.get(item_id)
-	elif item_type == 'teststep':
-		item = models.TestStepExecution.query.get(item_id)
-	else:
-		app.logger.warning(f'Item type "{item_type}" does not exist. Requested by "{current_user}".')
-		flash(f'Item type "{item_type}" does not exist.', 'warning')
-		return redirect(url_for('index'))
-
-	return render_template('testrun/item.html', type=item_type, item=item)
-
-
-@app.route('/<string:item_type>/<int:item_id>/delete', methods=['POST', 'DELETE'])
-@login_required
-def delete_item(item_type, item_id):
-	#
-	# delete item
-	#
-
-	# cascade delete
-	if request.method == 'POST':
-		try:
-			utils.deleteCascade(item_type, item_id)
-			flash(f'Item {utils.getItemType(item_type)} ID #{item_id} and its children have been successfully deleted.', 'success')
-			return 'Success', 200
-		except Exception as error:
-			flash(error, 'warning')
-			return 'Bad Request', 400
-
-	# delete single item
-	elif request.method == 'DELETE':
-		# get item by type and id
-		if item_type == 'testrun':
-			item = models.Testrun.query.get(item_id)
-		elif item_type == 'testcase_sequence':
-			item = models.TestCaseSequence.query.get(item_id)
-		elif item_type == 'testcase':
-			item = models.TestCase.query.get(item_id)
-		elif item_type == 'teststep_sequence':
-			item = models.TestStepSequence.query.get(item_id)
-		elif item_type == 'teststep':
-			item = models.TestStepExecution.query.get(item_id)
-		else:
-			app.logger.warning(f'Item type "{item_type}" does not exist. Requested by "{current_user}".')
-			flash(f'Item type "{item_type}" does not exist.', 'warning')
-			#return redirect(url_for('index'))
-			return 'Bad Request', 400
-
-		db.session.delete(item)
-		db.session.commit()
-		app.logger.info(f'Deleted {item_type} id {item_id} by {current_user}.')
-		flash(f'Item "{item.name}" has been successfully deleted.', 'success')
-		#return redirect(url_for('item_list', item_type=item_type))
-		return 'Success', 200
-
-	return 'Method Not Allowed', 405
-
-
-@app.route('/<string:item_type>/<int:item_id>/edit', methods=['GET', 'POST'])
-@login_required
-def edit_item(item_type, item_id):
-	#
-	# edit item
-	#
-	if item_type == 'testrun':
-		item = models.Testrun.query.get(item_id)
-		form = forms.TestrunCreateForm.new()
-		if request.method == 'GET':
-			form.testcase_sequences.data = [f'{x.id}' for x in item.testcase_sequences]
-	elif item_type == 'testcase_sequence':
-		item = models.TestCaseSequence.query.get(item_id)
-		form = forms.TestCaseSequenceCreateForm.new()
-		if request.method == 'GET':
-			form.classname.data = f'{item.classname.id}'
-			form.datafiles.data = [f'{x.id}' for x in item.datafiles]
-			form.testcases.data = [f'{x.id}' for x in item.testcases]
-	elif item_type == 'testcase':
-		item = models.TestCase.query.get(item_id)
-		form = forms.TestCaseCreateForm.new()
-		if request.method == 'GET':
-			form.classname.data = f'{item.classname.id}'
-			form.browser_type.data = f'{item.browser_type.id}'
-			form.testcase_type.data = f'{item.testcase_type.id}'
-			form.testcase_stepsequences.data = [f'{x.id}' for x in item.teststep_sequences]
-	elif item_type == 'teststep_sequence':
-		item = models.TestStepSequence.query.get(item_id)
-		form = forms.TestStepSequenceCreateForm.new()
-		if request.method == 'GET':
-			form.classname.data = f'{item.classname.id}'
-			form.teststeps.data =[f'{x.id}' for x in item.teststeps]
-	elif item_type == 'teststep':
-		item = models.TestStepExecution.query.get(item_id)
-		form = forms.TestStepCreateForm.new()
-		if request.method == 'GET':
-			form.activity_type.data = f'{item.activity_type.id}'
-			form.locator_type.data = f'{item.locator_type.id}'
-			# model extension
-			form.locator.data = item.locator or ''
-			if item.optional:
-				form.optional.data = '2'
-			else:
-				form.optional.data = '1'
-			if item.timeout:
-				form.timeout.data = f'{item.timeout}'
-			else:
-				form.timeout.data = ''
-			form.release.data = item.release or ''
-			form.value.data = item.value or ''
-			form.value2.data = item.value2 or ''
-			form.comparision.data = utils.getComparisionId(item.comparision)
-
-	else:
-		app.logger.warning(f'Item type "{item_type}" does not exist. Requested by "{current_user}".')
-		flash(f'Item type "{item_type}" does not exist.', 'warning')
-		return redirect(url_for('index'))
-
-	if request.method == 'GET':
-		form.name.data = item.name
-		form.description.data = item.description
-
-	if  form.validate_on_submit():
-		# update item data
-		item.name = form.name.data
-		item.description = form.description.data
-		# testrun
-		if item_type == 'testrun':
-			item.editor = current_user
-			item.edited = datetime.utcnow()
-			item.testcase_sequences=[models.TestCaseSequence.query.get(int(x)) for x in form.testcase_sequences.data]
-		# testcase sequence
-		elif item_type == 'testcase_sequence':
-			item.editor = current_user
-			item.edited = datetime.utcnow()
-			item.classname = models.ClassName.query.get(int(form.classname.data))
-			item.datafiles = [models.DataFile.query.get(int(x)) for x in form.datafiles.data]
-			item.testcases = [models.TestCase.query.get(int(x)) for x in form.testcases.data]
-		# testcase
-		elif item_type == 'testcase':
-			item.editor = current_user
-			item.edited = datetime.utcnow()
-			item.classname = models.ClassName.query.get(int(form.classname.data))
-			item.browser_type = models.BrowserType.query.get(int(form.browser_type.data))
-			item.testcase_type = models.TestCaseType.query.get(int(form.testcase_type.data))
-			item.teststep_sequences = [models.TestStepSequence.query.get(int(x)) for x in form.testcase_stepsequences.data]
-		# step sequence
-		elif item_type == 'teststep_sequence':
-			item.editor = current_user
-			item.edited = datetime.utcnow()
-			item.classname = models.ClassName.query.get(int(form.classname.data))
-			item.teststeps = [models.TestStepExecution.query.get(int(x)) for x in form.teststeps.data]
-		# test step
-		elif item_type == 'teststep':
-			item.editor = current_user
-			item.edited = datetime.utcnow()
-			item.activity_type = models.ActivityType.query.get(int(form.activity_type.data))
-			item.locator_type = models.LocatorType.query.get(int(form.locator_type.data))
-			# model extension
-			item.locator = form.locator.data
-			item.optional = [None, False, True][int(form.optional.data)]
-			try:
-				item.timeout = float(form.timeout.data)
-			except ValueError:
-				item.timeout = None
-			item.release = form.release.data or None
-			item.value = form.value.data or None
-			item.value2 = form.value2.data or None
-			if form.comparision.data == '0':
-				item.comparision = None
-			else:
-				item.comparision = utils.COMPARISIONS[int(form.comparision.data)-1]
-			
-
-		# update item in db
-		db.session.commit()
-		app.logger.info(f'Edited {item_type} id {item_id} by {current_user}.')
-		flash(f'Item "{item.name}" successfully updated.', 'success')
-		return redirect(url_for('item_list', item_type=item_type))
-
-
-	return render_template('testrun/edit_item.html', type=item_type, item=item, form=form)
-
-
-@app.route('/<string:item_type>/new', methods=['GET', 'POST'])
-@login_required
-def new_item(item_type):
-	#
-	# create new item
-	#
-	if item_type == 'testrun':
-		form = forms.TestrunCreateForm.new()
-		chips = ['testcase_sequences']
-	elif item_type == 'testcase_sequence':
-		form = forms.TestCaseSequenceCreateForm.new()
-		chips = ['datafiles', 'testcases']
-	elif item_type == 'testcase':
-		form = forms.TestCaseCreateForm.new()
-		chips = ['testcase_stepsequences']
-	elif item_type == 'teststep_sequence':
-		form = forms.TestStepSequenceCreateForm.new()
-		chips = ['teststeps']
-	elif item_type == 'teststep':
-		form = forms.TestStepCreateForm.new()
-		chips = []
-	else:
-		app.logger.warning(f'Item type "{item_type}" does not exist. Requested by "{current_user}".')
-		flash(f'Item type "{item_type}" does not exist.', 'warning')
-		return redirect(url_for('index'))
-
-	if form.validate_on_submit():
-		# create new item
-		# testrun
-		if item_type == 'testrun':
-			item = models.Testrun(
-				name=form.name.data,
-				description=form.description.data,
-				creator=current_user,
-				testcase_sequences=[models.TestCaseSequence.query.get(int(x)) for x in form.testcase_sequences.data],
-			)
-		# testcase sequence
-		elif item_type == 'testcase_sequence':
-			item = models.TestCaseSequence(
-				name=form.name.data,
-				description=form.description.data,
-				creator=current_user,
-				classname=models.ClassName.query.get(int(form.classname.data)),
-				datafiles=[models.DataFile.query.get(int(x)) for x in form.datafiles.data],
-				testcases=[models.TestCase.query.get(int(x)) for x in form.testcases.data],
-			)
-		# testcase
-		elif item_type == 'testcase':
-			item = models.TestCase(
-				name=form.name.data,
-				description=form.description.data,
-				creator=current_user,
-				classname=models.ClassName.query.get(int(form.classname.data)),
-				browser_type=models.BrowserType.query.get(int(form.browser_type.data)),
-				testcase_type=models.TestCaseType.query.get(int(form.testcase_type.data)),
-				teststep_sequences=[models.TestStepSequence.query.get(int(x)) for x in form.testcase_stepsequences.data],
-			)
-		# step sequence
-		elif item_type == 'teststep_sequence':
-			item = models.TestStepSequence(
-				name=form.name.data,
-				description=form.description.data,
-				creator=current_user,
-				classname=models.ClassName.query.get(int(form.classname.data)),
-				teststeps=[models.TestStepExecution.query.get(int(x)) for x in form.teststeps.data],
-			)
-		# test step
-		elif item_type == 'teststep':
-			item = models.TestStepExecution(
-				name=form.name.data,
-				description=form.description.data,
-				creator=current_user,
-				activity_type=models.ActivityType.query.get(int(form.activity_type.data)),
-				locator_type=models.LocatorType.query.get(int(form.locator_type.data)),
-				# model extension
-				locator=form.locator.data,
-				optional=[None, False, True][int(form.optional.data)],
-			)
-			try:
-				item.timeout = float(form.timeout.data)
-			except ValueError:
-				item.timeout = None
-			item.release = form.release.data or None
-			item.value = form.value.data or None
-			item.value2 = form.value2.data or None
-			if form.comparision.data == '0':
-				item.comparision = None
-			else:
-				item.comparision = utils.COMPARISIONS[int(form.comparision.data)-1]
-
-		# save item to db
-		db.session.add(item)
-		db.session.commit()
-		app.logger.info(f'Created {item_type} id {item.id} by {current_user}.')
-		flash(f'Item "{item.name}" successfully created.', 'success')
-		return redirect(url_for('item_list', item_type=item_type))
-
-	return render_template('testrun/create_item.html', type=item_type, chips=chips, form=form)
-	#return render_template('testrun/edit_item.html', type=item_type, item=None, form=form)
-
-
-@app.route('/testrun/xlsx/<int:item_id>', methods=['GET', 'POST'])
-@login_required
-def to_xlsx(item_id):
-	#
-	# export Testrun object to XLSX
-	#
-
-	result = utils.exportXLSX(item_id)
-	url = url_for('static', filename=f'files/{result}')
-
-	#flash(f'Testrun #{item_id} successfully exported to XLSX.', 'success')
-	#return redirect(url_for('item_list', item_type='testrun'))
-	app.logger.info(f'Imported Testrun id {item_id} by {current_user}. Target: static/{filename}')
-	return f'Success: <a href="{url}">{result}</a>' 
-
-@app.route('/testrun/import', methods=['POST'])
-@login_required
-def import_testsun():
-	#
-	# imports testrun from file
-	#
-
-	# import only from XLSX is available now
-	form = forms.TestrunImportForm()
-
-	if form.validate_on_submit():
-		#utils.importXLSX(form.file.data)
-		try:
-			utils.importXLSX(form.file.data)
-			app.logger.info(f'Testrun successfully imported from "{form.file.data.filename}" by {current_user}.')
-			flash(f'Testrun successfully imported from "{form.file.data.filename}"', 'success')
-		except Exception as error:
-			app.logger.error(f'Failed to import Testrun from "{form.file.data.filename}" by {current_user}. {error}.')
-			flash(f'ERROR: Cannot import Testrun from "{form.file.data.filename}". {error}.', 'danger')
-	else:
-		flash(f'File is required for import', 'warning')
-
-	return redirect(url_for('item_list', item_type='testrun'))
-
-
-@app.route('/testrun/<int:item_id>/import', methods=['POST'])
-@login_required
-def update_testsun(item_id):
-	#
-	# imports testrun from file
-	#
-
-	# update only from XLSX is available now
-	form = forms.TestrunImportForm()
-
-	if form.validate_on_submit():
-		print('******** FORM:')
-		for field in form:
-			print(f'{field.name}:\t{field.data}')
-		# update items
-		#utils.importXLSX(form.file.data, item_id=item_id)
-		try:
-			utils.importXLSX(form.file.data, item_id=item_id)
-			app.logger.info(f'Testrun ID #{item_id} successfully updated from "{form.file.data.filename}" by {current_user}.')
-			flash(f'Testrun ID #{item_id} has been successfully updated from "{form.file.data.filename}"', 'success')
-		except Exception as error:
-			app.logger.error(f'Failed to update Testrun ID #{item_id} from "{form.file.data.filename}" by {current_user}. {error}.')
-			flash(f'ERROR: Cannot update Testrun ID #{item_id} from "{form.file.data.filename}". {error}.', 'danger')
-	else:
-		flash(f'File is required for import', 'warning')
-
-	return redirect(url_for('item_list', item_type='testrun'))
-	
-
-#
-# user authentication
-#
-
-@app.route('/signup', methods=['GET', 'POST'])
-def signup():
-	if current_user.is_authenticated:
-		return redirect(url_for('index'))
-
-	form = forms.SingupForm()
-	if form.validate_on_submit():
-		# create user
-		user = models.User(username=form.username.data)
-		user.set_password(form.password.data)
-		db.session.add(user)
-		db.session.commit()
-		# login
-		login_user(user, remember=True)
-		flash(f'User {user.username.capitalize()} successfully created!', 'succsess')
-		return redirect(url_for('index'))
-
-	return render_template('testrun/signup.html', form=form)
-
-@app.route('/login', methods=['GET', 'POST'])
-def login():
-	if current_user.is_authenticated:
-		return redirect(url_for('index'))
-
-	form = forms.LoginForm()
-	if form.validate_on_submit():
-		user = models.User.query.filter_by(username=form.username.data).first()
-		if user and user.verify_password(form.password.data):
-			login_user(user, remember=True)
-			flash(f'Welcome {user.username.capitalize()}!', 'success')
-			return redirect(url_for('index'))
-
-	return render_template('testrun/login.html', form=form)
-
-@app.route('/logout')
-def logout():
-	logout_user()
-
-	return redirect(url_for('login'))
-	

+ 0 - 1
flask/application.py

@@ -1 +0,0 @@
-from app import app

+ 0 - 28
flask/config.py

@@ -1,28 +0,0 @@
-import os
-
-
-#
-# configuration settings
-#
-
-class Config(object):
-	#
-	# secret 
-	#
-	SECRET_KEY = os.getenv('SECRET_KEY') or 'secret!key'
-
-	#
-	# SQL Alchemy
-	#
-	SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL') or \
-    	'sqlite:///' + os.path.join(os.path.abspath(os.path.dirname(__file__)), 'app/testrun.db')
-	SQLALCHEMY_TRACK_MODIFICATIONS = False
-
-	#
-	# Testrun web-service
-	#
-	TESTRUN_SERVICE_HOST = os.getenv('TESTRUN_SERVICE_HOST') or 'http://127.0.0.1:6000'
-	TESTRUN_SERVICE_URI = 'api/json/json'
-
-
-	

+ 0 - 24
flask/define_steps.py

@@ -1,24 +0,0 @@
-import pandas as pd
-import json
-
-path_to_excel = '../BaangtDBFill.xlsx'
-path_to_json = 'teststeps.json'
-
-xl = pd.ExcelFile(path_to_excel)
-
-df = xl.parse('data')
-
-#print(df)
-steps = []
-for i in range(len(df)):
-	step = {
-		'name': df['Name'][i],
-		'description': df['Description'][i],
-		'activity_type': df['ActivityType'][i],
-		'locator_type': 'xpath',
-	}
-	steps.append(step)
-
-# dump to json
-with open(path_to_json, 'w') as f:
-	json.dump(steps, f)

+ 0 - 29
flask/define_testrun.py

@@ -1,29 +0,0 @@
-import pandas as pd
-import json
-
-path_to_excel = '../DropsTestRunDefinition.xlsx'
-path_to_json = 'testrun.json'
-
-xl = pd.ExcelFile(path_to_excel)
-
-# parse TestCaseSequence
-df = xl.parse('TestCaseSequence')
-
-for name in df:
-	print(name)
-
-'''
-testrun = {}
-for i in range(len(df)):
-	step = {
-		'name': df['Name'][i],
-		'description': df['Description'][i],
-		'activity_type': df['ActivityType'][i],
-		'locator_type': 'xpath',
-	}
-	steps.append(step)
-
-# dump to json
-with open(path_to_json, 'w') as f:
-	json.dump(steps, f)
-'''

+ 0 - 28
flask/discussion.txt

@@ -1,28 +0,0 @@
-First: In the TestSteps apart from the locatoryType should also be the locator - otherwise it won't work :)
-Also there are new fields on this level: Optional (boolean), Timeout (dec 5,2), Release (string), value2 (String), comparison (string). You can see the fields for instance in SimpleTheInternet.xlsx. I guess that shouldn't be a big problem :)
-The assignment of elements to other elements though is not ideal. Think of a bank with 100 systems, 1000 Testruns and 100.000 Teststeps. As I see the assignment from one level to the next is simply by clicking on a (full) list of elements of the lower level. I think this needs another idea :)
-About testing the functionality: There is a repository baangt-Docker that uses VNC and has all necessary dependencies installed (Firefox, Geckodriver, etc.). This should be a good base for testing. 
-anyway. About interfacing the database with the testrun: In the same way as now in TestRun.py
-called from __init__ there are methods _loadJSONTestRunDefinition and _loadExcelTestRunDefinition. Either you export the structures below a given testrun from the database into JSON-format, that can be read by this method OR we make a new method like _loadDbTestRunDefinition where the DB-Structures are filled into Dict.
-
-
-
-update 03/02/2020
-We’ll need a cascade delete functionality though. When a parent-object is deleted, ask “Delete all child objects?” and if yes, delete all child objects, that aren’t used in other parents. That’s a quick fix. Ultimately we’ll need kind of “Update from XLSX” in TestRun.
-
-The idea is as follows:
-
--) Business department creates a test case (by recording Web-Interactions using recorder and then using Baangt importer to create simpleFormat XLSX from the recording. This also creates data file structure.)
--) Business department fills in test data into the data file structure and tests with the test cases (baangt Interactive Starter or Baangt CLI)
--) Business department is happy with it and hands over to IT-Department (via XLSX)
--) IT-Department imports XLSX into baangtDB (the flask app)
--) IT-Department replaces common parts (e.g. Login) with existing objects (e.g. an existing TestStepSequence)
--) IT-Department runs it and is also happy
--) Sometime later something changes and IT-Department is not sure, what to do (“is it a bug or is it a feature?”)
--) they Export XLSX and send to business department (XLSX now has complex format)
--) Business department fixes the problem in the test case definition
--) Business department sends back the XLSX to IT-Department
-
-In our current solution IT-Department will delete the TestRun and import the XLSX. That’s not ideal as all historic information is lost. Better (not for now – but somewhen in the future) would be to update/modify from the XSLX.
-
- 

+ 0 - 1
flask/migrations/README

@@ -1 +0,0 @@
-Generic single-database configuration.

+ 0 - 45
flask/migrations/alembic.ini

@@ -1,45 +0,0 @@
-# A generic, single database configuration.
-
-[alembic]
-# template used to generate migration files
-# file_template = %%(rev)s_%%(slug)s
-
-# set to 'true' to run the environment during
-# the 'revision' command, regardless of autogenerate
-# revision_environment = false
-
-
-# Logging configuration
-[loggers]
-keys = root,sqlalchemy,alembic
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = WARN
-handlers = console
-qualname =
-
-[logger_sqlalchemy]
-level = WARN
-handlers =
-qualname = sqlalchemy.engine
-
-[logger_alembic]
-level = INFO
-handlers =
-qualname = alembic
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(levelname)-5.5s [%(name)s] %(message)s
-datefmt = %H:%M:%S

+ 0 - 96
flask/migrations/env.py

@@ -1,96 +0,0 @@
-from __future__ import with_statement
-
-import logging
-from logging.config import fileConfig
-
-from sqlalchemy import engine_from_config
-from sqlalchemy import pool
-
-from alembic import context
-
-# this is the Alembic Config object, which provides
-# access to the values within the .ini file in use.
-config = context.config
-
-# Interpret the config file for Python logging.
-# This line sets up loggers basically.
-fileConfig(config.config_file_name)
-logger = logging.getLogger('alembic.env')
-
-# add your model's MetaData object here
-# for 'autogenerate' support
-# from myapp import mymodel
-# target_metadata = mymodel.Base.metadata
-from flask import current_app
-config.set_main_option(
-    'sqlalchemy.url', current_app.config.get(
-        'SQLALCHEMY_DATABASE_URI').replace('%', '%%'))
-target_metadata = current_app.extensions['migrate'].db.metadata
-
-# other values from the config, defined by the needs of env.py,
-# can be acquired:
-# my_important_option = config.get_main_option("my_important_option")
-# ... etc.
-
-
-def run_migrations_offline():
-    """Run migrations in 'offline' mode.
-
-    This configures the context with just a URL
-    and not an Engine, though an Engine is acceptable
-    here as well.  By skipping the Engine creation
-    we don't even need a DBAPI to be available.
-
-    Calls to context.execute() here emit the given string to the
-    script output.
-
-    """
-    url = config.get_main_option("sqlalchemy.url")
-    context.configure(
-        url=url, target_metadata=target_metadata, literal_binds=True
-    )
-
-    with context.begin_transaction():
-        context.run_migrations()
-
-
-def run_migrations_online():
-    """Run migrations in 'online' mode.
-
-    In this scenario we need to create an Engine
-    and associate a connection with the context.
-
-    """
-
-    # this callback is used to prevent an auto-migration from being generated
-    # when there are no changes to the schema
-    # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
-    def process_revision_directives(context, revision, directives):
-        if getattr(config.cmd_opts, 'autogenerate', False):
-            script = directives[0]
-            if script.upgrade_ops.is_empty():
-                directives[:] = []
-                logger.info('No changes in schema detected.')
-
-    connectable = engine_from_config(
-        config.get_section(config.config_ini_section),
-        prefix='sqlalchemy.',
-        poolclass=pool.NullPool,
-    )
-
-    with connectable.connect() as connection:
-        context.configure(
-            connection=connection,
-            target_metadata=target_metadata,
-            process_revision_directives=process_revision_directives,
-            **current_app.extensions['migrate'].configure_args
-        )
-
-        with context.begin_transaction():
-            context.run_migrations()
-
-
-if context.is_offline_mode():
-    run_migrations_offline()
-else:
-    run_migrations_online()

+ 0 - 24
flask/migrations/script.py.mako

@@ -1,24 +0,0 @@
-"""${message}
-
-Revision ID: ${up_revision}
-Revises: ${down_revision | comma,n}
-Create Date: ${create_date}
-
-"""
-from alembic import op
-import sqlalchemy as sa
-${imports if imports else ""}
-
-# revision identifiers, used by Alembic.
-revision = ${repr(up_revision)}
-down_revision = ${repr(down_revision)}
-branch_labels = ${repr(branch_labels)}
-depends_on = ${repr(depends_on)}
-
-
-def upgrade():
-    ${upgrades if upgrades else "pass"}
-
-
-def downgrade():
-    ${downgrades if downgrades else "pass"}

+ 0 - 32
flask/migrations/versions/17cfc1a6b077_.py

@@ -1,32 +0,0 @@
-"""empty message
-
-Revision ID: 17cfc1a6b077
-Revises: c7fc082b1d55
-Create Date: 2020-02-16 18:15:26.010971
-
-"""
-from alembic import op
-import sqlalchemy as sa
-
-
-# revision identifiers, used by Alembic.
-revision = '17cfc1a6b077'
-down_revision = 'c7fc082b1d55'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    op.alter_column('teststep_executions', 'optional',
-               existing_type=sa.BOOLEAN(),
-               nullable=False)
-    # ### end Alembic commands ###
-
-
-def downgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    op.alter_column('teststep_executions', 'optional',
-               existing_type=sa.BOOLEAN(),
-               nullable=True)
-    # ### end Alembic commands ###

+ 0 - 212
flask/migrations/versions/afb044a83b98_.py

@@ -1,212 +0,0 @@
-"""empty message
-
-Revision ID: afb044a83b98
-Revises: 
-Create Date: 2020-02-03 16:38:12.041603
-
-"""
-from alembic import op
-import sqlalchemy as sa
-
-
-# revision identifiers, used by Alembic.
-revision = 'afb044a83b98'
-down_revision = None
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    op.create_table('activity_types',
-    sa.Column('id', sa.Integer(), nullable=False),
-    sa.Column('name', sa.String(length=64), nullable=False),
-    sa.Column('description', sa.String(length=512), nullable=False),
-    sa.PrimaryKeyConstraint('id')
-    )
-    op.create_table('browser_types',
-    sa.Column('id', sa.Integer(), nullable=False),
-    sa.Column('name', sa.String(length=64), nullable=False),
-    sa.Column('description', sa.String(length=512), nullable=False),
-    sa.PrimaryKeyConstraint('id')
-    )
-    op.create_table('classnames',
-    sa.Column('id', sa.Integer(), nullable=False),
-    sa.Column('name', sa.String(length=64), nullable=False),
-    sa.Column('description', sa.String(length=512), nullable=False),
-    sa.PrimaryKeyConstraint('id')
-    )
-    op.create_table('locator_types',
-    sa.Column('id', sa.Integer(), nullable=False),
-    sa.Column('name', sa.String(length=64), nullable=False),
-    sa.Column('description', sa.String(length=512), nullable=False),
-    sa.PrimaryKeyConstraint('id')
-    )
-    op.create_table('testcase_types',
-    sa.Column('id', sa.Integer(), nullable=False),
-    sa.Column('name', sa.String(length=64), nullable=False),
-    sa.Column('description', sa.String(length=512), nullable=False),
-    sa.PrimaryKeyConstraint('id')
-    )
-    op.create_table('users',
-    sa.Column('id', sa.Integer(), nullable=False),
-    sa.Column('username', sa.String(length=64), nullable=False),
-    sa.Column('password', sa.String(length=128), nullable=False),
-    sa.Column('created', sa.DateTime(), nullable=False),
-    sa.Column('lastlogin', sa.DateTime(), nullable=False),
-    sa.PrimaryKeyConstraint('id'),
-    sa.UniqueConstraint('username')
-    )
-    op.create_table('datafiles',
-    sa.Column('id', sa.Integer(), nullable=False),
-    sa.Column('filename', sa.String(length=64), nullable=False),
-    sa.Column('created', sa.DateTime(), nullable=False),
-    sa.Column('creator_id', sa.Integer(), nullable=False),
-    sa.ForeignKeyConstraint(['creator_id'], ['users.id'], ),
-    sa.PrimaryKeyConstraint('id')
-    )
-    op.create_table('testcase_sequences',
-    sa.Column('id', sa.Integer(), nullable=False),
-    sa.Column('name', sa.String(length=64), nullable=False),
-    sa.Column('description', sa.String(length=512), nullable=False),
-    sa.Column('created', sa.DateTime(), nullable=False),
-    sa.Column('creator_id', sa.Integer(), nullable=False),
-    sa.Column('edited', sa.DateTime(), nullable=True),
-    sa.Column('editor_id', sa.Integer(), nullable=True),
-    sa.Column('classname_id', sa.Integer(), nullable=False),
-    sa.ForeignKeyConstraint(['classname_id'], ['classnames.id'], ),
-    sa.ForeignKeyConstraint(['creator_id'], ['users.id'], ),
-    sa.ForeignKeyConstraint(['editor_id'], ['users.id'], ),
-    sa.PrimaryKeyConstraint('id')
-    )
-    op.create_table('testcases',
-    sa.Column('id', sa.Integer(), nullable=False),
-    sa.Column('name', sa.String(length=64), nullable=False),
-    sa.Column('description', sa.String(length=512), nullable=False),
-    sa.Column('created', sa.DateTime(), nullable=False),
-    sa.Column('creator_id', sa.Integer(), nullable=False),
-    sa.Column('edited', sa.DateTime(), nullable=True),
-    sa.Column('editor_id', sa.Integer(), nullable=True),
-    sa.Column('classname_id', sa.Integer(), nullable=False),
-    sa.Column('browser_type_id', sa.Integer(), nullable=False),
-    sa.Column('testcase_type_id', sa.Integer(), nullable=False),
-    sa.ForeignKeyConstraint(['browser_type_id'], ['browser_types.id'], ),
-    sa.ForeignKeyConstraint(['classname_id'], ['classnames.id'], ),
-    sa.ForeignKeyConstraint(['creator_id'], ['users.id'], ),
-    sa.ForeignKeyConstraint(['editor_id'], ['users.id'], ),
-    sa.ForeignKeyConstraint(['testcase_type_id'], ['testcase_types.id'], ),
-    sa.PrimaryKeyConstraint('id')
-    )
-    op.create_table('testruns',
-    sa.Column('id', sa.Integer(), nullable=False),
-    sa.Column('name', sa.String(length=64), nullable=False),
-    sa.Column('description', sa.String(length=512), nullable=False),
-    sa.Column('created', sa.DateTime(), nullable=False),
-    sa.Column('creator_id', sa.Integer(), nullable=False),
-    sa.Column('edited', sa.DateTime(), nullable=True),
-    sa.Column('editor_id', sa.Integer(), nullable=True),
-    sa.ForeignKeyConstraint(['creator_id'], ['users.id'], ),
-    sa.ForeignKeyConstraint(['editor_id'], ['users.id'], ),
-    sa.PrimaryKeyConstraint('id')
-    )
-    op.create_table('teststep_sequences',
-    sa.Column('id', sa.Integer(), nullable=False),
-    sa.Column('name', sa.String(length=64), nullable=False),
-    sa.Column('description', sa.String(length=512), nullable=False),
-    sa.Column('created', sa.DateTime(), nullable=False),
-    sa.Column('creator_id', sa.Integer(), nullable=False),
-    sa.Column('edited', sa.DateTime(), nullable=True),
-    sa.Column('editor_id', sa.Integer(), nullable=True),
-    sa.Column('classname_id', sa.Integer(), nullable=False),
-    sa.ForeignKeyConstraint(['classname_id'], ['classnames.id'], ),
-    sa.ForeignKeyConstraint(['creator_id'], ['users.id'], ),
-    sa.ForeignKeyConstraint(['editor_id'], ['users.id'], ),
-    sa.PrimaryKeyConstraint('id')
-    )
-    op.create_table('global_teststep_executions',
-    sa.Column('id', sa.Integer(), nullable=False),
-    sa.Column('name', sa.String(length=64), nullable=False),
-    sa.Column('description', sa.String(length=512), nullable=False),
-    sa.Column('created', sa.DateTime(), nullable=False),
-    sa.Column('creator_id', sa.Integer(), nullable=False),
-    sa.Column('edited', sa.DateTime(), nullable=True),
-    sa.Column('editor_id', sa.Integer(), nullable=True),
-    sa.Column('activity_type_id', sa.Integer(), nullable=False),
-    sa.Column('locator_type_id', sa.Integer(), nullable=False),
-    sa.Column('teststep_sequence_id', sa.Integer(), nullable=True),
-    sa.ForeignKeyConstraint(['activity_type_id'], ['activity_types.id'], ),
-    sa.ForeignKeyConstraint(['creator_id'], ['users.id'], ),
-    sa.ForeignKeyConstraint(['editor_id'], ['users.id'], ),
-    sa.ForeignKeyConstraint(['locator_type_id'], ['locator_types.id'], ),
-    sa.ForeignKeyConstraint(['teststep_sequence_id'], ['teststep_sequences.id'], ),
-    sa.PrimaryKeyConstraint('id')
-    )
-    op.create_table('testcase_sequence_case',
-    sa.Column('testcase_sequence_id', sa.Integer(), nullable=False),
-    sa.Column('testcase_id', sa.Integer(), nullable=False),
-    sa.ForeignKeyConstraint(['testcase_id'], ['testcases.id'], ),
-    sa.ForeignKeyConstraint(['testcase_sequence_id'], ['testcase_sequences.id'], ),
-    sa.PrimaryKeyConstraint('testcase_sequence_id', 'testcase_id')
-    )
-    op.create_table('testcase_sequence_datafile',
-    sa.Column('testcase_sequence_id', sa.Integer(), nullable=False),
-    sa.Column('datafile_id', sa.Integer(), nullable=False),
-    sa.ForeignKeyConstraint(['datafile_id'], ['datafiles.id'], ),
-    sa.ForeignKeyConstraint(['testcase_sequence_id'], ['testcase_sequences.id'], ),
-    sa.PrimaryKeyConstraint('testcase_sequence_id', 'datafile_id')
-    )
-    op.create_table('testcase_stepsequence',
-    sa.Column('testcase_id', sa.Integer(), nullable=False),
-    sa.Column('teststep_sequence_id', sa.Integer(), nullable=False),
-    sa.ForeignKeyConstraint(['testcase_id'], ['testcases.id'], ),
-    sa.ForeignKeyConstraint(['teststep_sequence_id'], ['teststep_sequences.id'], ),
-    sa.PrimaryKeyConstraint('testcase_id', 'teststep_sequence_id')
-    )
-    op.create_table('testrun_casesequence',
-    sa.Column('testrun_id', sa.Integer(), nullable=False),
-    sa.Column('testcase_sequence_id', sa.Integer(), nullable=False),
-    sa.ForeignKeyConstraint(['testcase_sequence_id'], ['testcase_sequences.id'], ),
-    sa.ForeignKeyConstraint(['testrun_id'], ['testruns.id'], ),
-    sa.PrimaryKeyConstraint('testrun_id', 'testcase_sequence_id')
-    )
-    op.create_table('teststep_executions',
-    sa.Column('id', sa.Integer(), nullable=False),
-    sa.Column('name', sa.String(length=64), nullable=False),
-    sa.Column('description', sa.String(length=512), nullable=False),
-    sa.Column('created', sa.DateTime(), nullable=False),
-    sa.Column('creator_id', sa.Integer(), nullable=False),
-    sa.Column('edited', sa.DateTime(), nullable=True),
-    sa.Column('editor_id', sa.Integer(), nullable=True),
-    sa.Column('activity_type_id', sa.Integer(), nullable=False),
-    sa.Column('locator_type_id', sa.Integer(), nullable=False),
-    sa.Column('teststep_sequence_id', sa.Integer(), nullable=True),
-    sa.ForeignKeyConstraint(['activity_type_id'], ['activity_types.id'], ),
-    sa.ForeignKeyConstraint(['creator_id'], ['users.id'], ),
-    sa.ForeignKeyConstraint(['editor_id'], ['users.id'], ),
-    sa.ForeignKeyConstraint(['locator_type_id'], ['locator_types.id'], ),
-    sa.ForeignKeyConstraint(['teststep_sequence_id'], ['teststep_sequences.id'], ),
-    sa.PrimaryKeyConstraint('id')
-    )
-    # ### end Alembic commands ###
-
-
-def downgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    op.drop_table('teststep_executions')
-    op.drop_table('testrun_casesequence')
-    op.drop_table('testcase_stepsequence')
-    op.drop_table('testcase_sequence_datafile')
-    op.drop_table('testcase_sequence_case')
-    op.drop_table('global_teststep_executions')
-    op.drop_table('teststep_sequences')
-    op.drop_table('testruns')
-    op.drop_table('testcases')
-    op.drop_table('testcase_sequences')
-    op.drop_table('datafiles')
-    op.drop_table('users')
-    op.drop_table('testcase_types')
-    op.drop_table('locator_types')
-    op.drop_table('classnames')
-    op.drop_table('browser_types')
-    op.drop_table('activity_types')
-    # ### end Alembic commands ###

+ 0 - 40
flask/migrations/versions/c7fc082b1d55_.py

@@ -1,40 +0,0 @@
-"""empty message
-
-Revision ID: c7fc082b1d55
-Revises: afb044a83b98
-Create Date: 2020-02-06 13:40:37.665770
-
-"""
-from alembic import op
-import sqlalchemy as sa
-
-
-# revision identifiers, used by Alembic.
-revision = 'c7fc082b1d55'
-down_revision = 'afb044a83b98'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    op.add_column('teststep_executions', sa.Column('comparision', sa.String(), nullable=True))
-    op.add_column('teststep_executions', sa.Column('locator', sa.String(), nullable=True))
-    op.add_column('teststep_executions', sa.Column('optional', sa.Boolean(), nullable=True))
-    op.add_column('teststep_executions', sa.Column('release', sa.String(), nullable=True))
-    op.add_column('teststep_executions', sa.Column('timeout', sa.Float(precision='5,2'), nullable=True))
-    op.add_column('teststep_executions', sa.Column('value', sa.String(), nullable=True))
-    op.add_column('teststep_executions', sa.Column('value2', sa.String(), nullable=True))
-    # ### end Alembic commands ###
-
-
-def downgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    op.drop_column('teststep_executions', 'value2')
-    op.drop_column('teststep_executions', 'value')
-    op.drop_column('teststep_executions', 'timeout')
-    op.drop_column('teststep_executions', 'release')
-    op.drop_column('teststep_executions', 'optional')
-    op.drop_column('teststep_executions', 'locator')
-    op.drop_column('teststep_executions', 'comparision')
-    # ### end Alembic commands ###

+ 0 - 329
flask/populate_db.py

@@ -1,329 +0,0 @@
-import json
-import xlrd
-from app import db
-from app.models import *
-
-#DATABASE_URL = 'sqlite:///testrun.db'
-#engine = create_engine(DATABASE_URL)
-
-# create a db.session
-#db.session = db.sessionmaker(bind=engine)
-#db.session = db.session()
-
-# recreate db structure
-db.drop_all()
-db.create_all()
-
-# create users
-print('Creating users...')
-admin = User(username='admin')
-admin.set_password('12345')
-user = User(username='simple_user')
-user.set_password('12345')
-db.session.add(admin)
-db.session.add(user)
-db.session.commit()
-print('Done.')
-
-# create supports
-print('Creating supports...')
-browsers = {
-	'FF': 'Mozilla Firefox',
-	'Chrome': 'Google Chrome',
-	'IE': 'MS Internet Exploer',
-	'Safari': 'Safari',
-	'Edge': 'MS Edge',
-}
-for key, value in browsers.items():
-	browser = BrowserType(name=key, description=value)
-	db.session.add(browser)
-
-testcases = {
-	'Browser': 'Browser',
-	'API-Rest': 'API-Rest',
-	'API-SOAP': 'API-SOAP',
-	'API-oDataV2': 'API-oDataV2',
-	'API-oDataV4': 'API-oDataV4',
-}
-for key, value in testcases.items():
-	testcase = TestCaseType(name=key, description=value)
-	db.session.add(testcase)
-
-activities = {
-	'GOTOURL': 'Go to an URL',
-	'SETTEXT': 'Set Text of an Element',
-	'CLICK': 'Click on an Element',
-	'COMMENT': 'A Simple Comment',
-}
-for key, value in activities.items():
-	activity = ActivityType(name=key, description=value)
-	db.session.add(activity)
-
-locators = {
-	'xpath': 'Locate via XPATH-Expression',
-	'css': 'Locate via CSS-Path of the Element',
-	'id': 'Locate via ID of the Element',
-}
-for key, value in locators.items():
-	locator = LocatorType(name=key, description=value)
-	db.session.add(locator)
-
-classnames = {
-	'Class A': 'A Simple Class Name',
-	'Class B': 'One more Simple Class Name',
-	'Class C': 'A Complex Class Name',
-}
-for key, value in classnames.items():
-	classname = ClassName(name=key, description=value)
-	db.session.add(classname)
-
-db.session.commit()
-print('Done.')
-
-# create mains
-print('Creating mains...')
-for i in range(5):
-	testrun  = Testrun(
-		name=f'Testrun #{i}',
-		description=f'Testrun #{i} is intended for testing the application UI. There are several features wich are described here.',
-		creator=admin,
-	)
-	db.session.add(testrun)
-
-db.session.commit()
-
-for i in range(5):
-	if i < 3:
-		u = admin
-	else:
-		u = user
-	testseq  = TestCaseSequence(
-		name=f'Test Case Sequence #{i}',
-		description=f'Test Case Sequence #{i} is intended for testing the application UI. There are several features wich are described here.',
-		creator=u,
-		classname=classname,
-	)
-	testseq.testrun.append(testrun)
-	if i == 2 or i == 3:
-		another_testrun = Testrun.query.get(i)
-		testseq.testrun.append(another_testrun)
-	db.session.add(testseq)
-	
-db.session.commit()
-
-for i in range(5):
-	if i < 3:
-		u = admin
-	else:
-		u = user
-	datafile  = DataFile(
-		filename=f'data_file_{i}.xlsx',
-		creator=u,
-	)
-	testseq = TestCaseSequence.query.get(i+1)
-	datafile.testcase_sequence.append(testseq)
-	db.session.add(datafile)
-	
-db.session.commit()
-
-# get supports
-browsers = BrowserType.query.all()
-testtypes = TestCaseType.query.all()
-activities = ActivityType.query.all()
-locators = LocatorType.query.all()
-
-for i in range(12):
-	if i%3 == 0:
-		u = admin
-	else:
-		u = user
-	testcase  = TestCase(
-		name=f'Test Case #{i}',
-		description=f'Test Case #{i} is intended for testing the application UI. There are several features wich are described here.',
-		creator=u,
-		classname=classname,
-		browser_type=browsers[i%len(browsers)],
-		testcase_type=testtypes[i%len(testtypes)], 
-	)
-	testseq = TestCaseSequence.query.get(i%5 + 1)
-	testcase.testcase_sequence.append(testseq)
-	db.session.add(testcase)
-
-db.session.commit()
-
-for i in range(7):
-	if i%3 == 0:
-		u = admin
-	else:
-		u = user
-	teststepseq  = TestStepSequence(
-		name=f'Test Step Sequence #{i}',
-		description=f'Test Step Sequence #{i} is intended for testing the application UI. There are several features wich are described here.',
-		creator=u,
-		classname=classname,
-	)
-	teststepseq.testcase.append(testcase)
-	db.session.add(testcase)
-
-db.session.commit()
-
-'''
-for i in range(4):
-	if i%3 == 0:
-		u = admin
-	else:
-		u = user
-	teststepex  = TestStepExecution(
-		name=f'Test Step Execution #{i}',
-		description=f'Test Step Execution #{i} is intended for testing the application UI. There are several features wich are described here.',
-		creator=u,
-		activity_type=activities[i%len(activities)],
-		locator_type=locators[i%len(locators)],
-		teststep_sequence=TestStepSequence.query.get(i%2 + 1),
-	)
-	db.session.add(testcase)
-'''
-# get teststeps from json
-print('Creating from JSON...')
-path_to_json = 'teststeps.json'
-with open(path_to_json, 'r') as f:
-	steps = json.load(f)
-# populate teststeps
-for step in steps:
-	# get activity type
-	for a in activities:
-		if a.name.upper() == step.get('activity_type').upper():
-			activity = a
-			break
-	# get locator type
-	for l in locators:
-		if l.name.upper() == step.get('locator_type').upper():
-			locator = l
-			break
-	# create Test Step
-	teststepex  = TestStepExecution(
-		name=step.get('name'),
-		description=step.get('description'),
-		creator=admin,
-		activity_type=activity,
-		locator_type=locator,
-	)
-	db.session.add(testcase)
-
-db.session.commit()
-
-# set Sample Drop items
-print('Creating Sample Drop Items...')
-testrun  = Testrun(
-	name=f'Sample Drop Testrun',
-	description=f'Sample Drop Testrun from "DropsTestRunDefinition.xlsx"',
-	creator=admin,
-)
-db.session.add(testrun)
-db.session.commit()
-
-classname = ClassName(
-	name='GC.CLASSES_TESTCASESEQUENCE',
-	description='Classname for Sample Drop Test Case Sequence')
-db.session.add(classname)
-datafile  = DataFile(
-		filename=f'DropsTestExample.xlsx',
-		creator=admin,
-	)
-db.session.add(datafile)
-db.session.commit()
-testseq  = TestCaseSequence(
-	name=f'Sample Drop Test Case Sequence',
-	description=f'Sample Drop Test Case Sequence from "DropsTestRunDefinition.xlsx"',
-	creator=admin,
-	classname=classname,
-)
-testseq.testrun.append(testrun)
-testseq.datafiles.append(datafile)
-db.session.add(testseq)
-db.session.commit()
-
-
-classname = ClassName(
-	name='GC.CLASSES_TESTCASE',
-	description='Classname for Sample Drop Test Case')
-db.session.add(classname)
-db.session.commit()
-testcase  = TestCase(
-	name=f'Sample Drop Test Case',
-	description=f'Sample Drop Test Case from "DropsTestRunDefinition.xlsx"',
-	creator=admin,
-	classname=classname,
-	browser_type=BrowserType.query.get(1),
-	testcase_type=TestCaseType.query.get(1), 
-)
-testcase.testcase_sequence.append(testseq)
-db.session.add(testcase)
-db.session.commit()
-
-classname = ClassName(
-	name='GC.CLASSES_TESTSTEPMASTER',
-	description='Classname for Sample Drop Test Step')
-db.session.add(classname)
-db.session.commit()
-teststepseq  = TestStepSequence(
-	name=f'Sample Drop Test Step',
-	description=f'Sample Drop Test Case from "DropsTestRunDefinition.xlsx"',
-	creator=admin,
-	classname=classname,
-)
-teststepseq.testcase.append(testcase)
-db.session.add(testcase)
-db.session.commit()
-
-# 1
-teststepex  = TestStepExecution(
-	name=f'Sample Drop Test Step Execution Number 1',
-	description=f'Sample Drop Test Step Execution Number 1 from "DropsTestRunDefinition.xlsx"',
-	creator=admin,
-	teststep_sequence=teststepseq,
-	activity_type=ActivityType.query.get(1),
-	locator_type=LocatorType.query.get(1),
-	value = 'https://drops.earthsquad.global',
-)
-db.session.add(teststepex)
-# 2
-teststepex  = TestStepExecution(
-	name=f'Sample Drop Test Step Execution Number 2',
-	description=f'Sample Drop Test Step Execution Number 2 from "DropsTestRunDefinition.xlsx"',
-	creator=admin,
-	teststep_sequence=teststepseq,
-	activity_type=ActivityType.query.get(2),
-	locator_type=LocatorType.query.get(1),
-	locator = "(//input[@step='any'])[1]",
-	value = '$(Username)',
-	timeout = 5,
-)
-db.session.add(teststepex)
-# 3
-teststepex  = TestStepExecution(
-	name=f'Sample Drop Test Step Execution Number 3',
-	description=f'Sample Drop Test Step Execution Number 3 from "DropsTestRunDefinition.xlsx"',
-	creator=admin,
-	teststep_sequence=teststepseq,
-	activity_type=ActivityType.query.get(2),
-	locator_type=LocatorType.query.get(1),
-	locator = "(//input[@step='any'])[2]",
-	value = '$(Password)',
-	timeout = 0.2,
-)
-db.session.add(teststepex)
-# 4
-teststepex  = TestStepExecution(
-	name=f'Sample Drop Test Step Execution Number 4',
-	description=f'Sample Drop Test Step Execution Number 4 from "DropsTestRunDefinition.xlsx"',
-	creator=admin,
-	teststep_sequence=teststepseq,
-	activity_type=ActivityType.query.get(3),
-	locator_type=LocatorType.query.get(1),
-	locator = "//button[contains(.,'Submit')]",
-)
-db.session.add(teststepex)
-db.session.commit()
-
-print('Done.')

+ 0 - 8
flask/requirements.txt

@@ -1,8 +0,0 @@
-flask>=1.1.1
-sqlalchemy>=1.3.12
-flask_sqlalchemy>=2.4.1
-flask_login>=0.4.1
-flask_wtf>=0.14.2
-flask_migrate>=2.5.2
-xlsxwriter>=1.2.8
-xlrd>=1.2.0

+ 0 - 9
flask/start_docker.sh

@@ -1,9 +0,0 @@
-docker build -t testrun .
-echo .....
-echo User = admin, password = 12345 . goto http://localhost:5000
-echo .....
-echo To stop docker:
-echo docker ps
-echo docker stop _name_of_dockerimage_
-echo .....
-docker run -d -p 5000:5000 testrun

File diff suppressed because it is too large
+ 0 - 1
flask/teststeps.json


File diff suppressed because it is too large
+ 1 - 0
jsons/results_SimpleTheInternet.json


File diff suppressed because it is too large
+ 1 - 0
jsons/results_example_googleImages.json


File diff suppressed because it is too large
+ 1 - 0
jsons/results_paypal_secondform2.json


File diff suppressed because it is too large
+ 0 - 2433
windows/baangtSetupWindows.iss


+ 0 - 38
windows/baangtWindows.spec

@@ -1,38 +0,0 @@
-# -*- mode: python ; coding: utf-8 -*-
-
-block_cipher = None
-
-a = Analysis(['..\\baangtIA.py'],
-             pathex=['windows'],
-             binaries=[],
-             datas=[('../baangt/ressources/baangtLogo2020Small.png', 'ressources'),
-                    ('../examples/', 'examples'),
-                    ('../browsermob-proxy','browsermob-proxy')],
-             hiddenimports=[],
-             hookspath=[],
-             runtime_hooks=[],
-             excludes=[],
-             win_no_prefer_redirects=False,
-             win_private_assemblies=False,
-             cipher=block_cipher,
-             noarchive=False)
-pyz = PYZ(a.pure, a.zipped_data,
-             cipher=block_cipher)
-exe = EXE(pyz,
-          a.scripts,
-          [],
-          exclude_binaries=True,
-          name='baangt',
-          debug=False,
-          bootloader_ignore_signals=False,
-          strip=False,
-          upx=True,
-          console=True )
-coll = COLLECT(exe,
-               a.binaries,
-               a.zipfiles,
-               a.datas,
-               strip=False,
-               upx=True,
-               upx_exclude=[],
-               name='baangt')