I am trying to test a basic loginscreen (created using tornadofx) with the testfx framework.
I have added 3 test cases which runs fine but the problem is they use the previous stage rather than creating a new one. I want the testcases to run independently.
I am testing a View() and not an App(). If I use MyMainApp().start(stage) and then MyMainApp().stop(), I get the required behaviour.
But how to do this for Views and Fragments.
Below is the code:
class LoginScreenFeatureTest : ApplicationTest() {
override fun init() {
FxToolkit.registerStage { Stage() }
}
override fun start(stage: Stage) {
LoginScreen().openWindow()
//MyMainApp().start(stage)
}
override fun stop() {
FxToolkit.cleanupStages()
//FxToolkit.toolkitContext().registeredStage.close()
//MyMainApp().stop()
}
#Test fun should_contain_button() {
// expect:
verifyThat("#submitBut", hasText("SUBMIT"))
}
#Test fun should_click_on_button_and_pass_login() {
//init
//Why do I always need to erase text. I want a new stage for every test case.
clickOn("#username").eraseText(10).write("validUser")
clickOn("#password").eraseText(10).write("validPwd")
clickOn("#orgId").eraseText(10).write("validOrg")
// when:
clickOn("#submitBut")
// then:
//verify success
}
#Test fun should_click_on_button_and_fail_login() {
//init
clickOn("#username").eraseText(10).write("anyuser")
clickOn("#password").eraseText(10).write("anypwd")
clickOn("#orgId").eraseText(10).write("anyorg")
// when:
clickOn("#submitBut")
// then:
//verify fail
}
}
You can add property which you can edit at any time at you App() class.
class MyMainApp: App() {
override val primaryView: KClass<out View> = primaryViewMyApp
companion object {
var primaryViewMyApp: KClass<out View> = MyMainAppView::class
}
init {
importStylesheet(<your stylesheet>)
}
override fun start(stage: Stage) {
super.start(stage)
}
override fun stop() {
super.stop()
}
}
and in the test you can than use any view you want to use. I didnt try to implement something for Fragment so far...
override fun init() {
FxToolkit.registerStage { Stage() }
}
override fun start(stage: Stage) {
MyMainApp.primaryViewGoApp = <your view>::class
MyMainApp().start(stage)
}
override fun stop() {
FxToolkit.cleanupStages()
MyMainApp().stop()
}
Related
I have 2 Flowables (one which is giving me VelocityNed items, and other which I written to consume items from first one); the thing is I don't know how to make the second one right, since I still not feel sure with RxJava
my Flowable code:
private Flowable<Float> getIAS(Flowable<VelocityNed> velocityNed) {
Flowable<Float> flowable = Flowable.create(emitter->{
velocityNed.subscribeWith(new DisposableSubscriber<VelocityNed>() {
#Override public void onNext(VelocityNed v) {
float valueToEmit = (float)Math.sqrt(Math.pow(v.getDownMS(),2)+Math.pow(v.getEastMS(),2)+Math.pow(v.getNorthMS(),2));
//how to emit this
}
#Override public void onError(Throwable t) {
t.printStackTrace();
}
#Override public void onComplete() {
emitter.onComplete();
this.dispose();
}
});
}, BackpressureStrategy.BUFFER);
return flowable;
}
You don't need to create a Flowable manually just to transform the emissions. You can do originalFlowable.map(element -> { transform element however you want }).
In your case it would be something like:
Flowable<Float> flowable = velocityNed.map(v -> {
(float)Math.sqrt(Math.pow(v.getDownMS(),2)+Math.pow(v.getEastMS(),2)+Math.pow(v.getNorthMS(),2));
})
I was taking a look at this :
tornadofx
and tried to expand on it with database connection and little more options, (not all of them make sense, but its just playing in a sandbox).
Even though table can be directly edited and the data will persist in database, i did try to do edit through text fields too. actual table editing would happen through different view and not table itself, as i said its just example.
Database used is Jetbrains Exposed.
object Categories : IntIdTable() {
val name = varchar("name", 64).uniqueIndex()
val description = varchar("description", 128)
}
class Category(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<Category>(Categories)
var name by Categories.name
var description by Categories.description
override fun toString(): String {
return "Category(name=\"$name\", description=\"$description\")"
}
}
now controller looks something like this, functions are just rudimentary and picked as an example.
typealias ModelToDirtyState = Map.Entry<CategoryModel, TableColumnDirtyState<CategoryModel>>
class CategoryModel() : ItemViewModel<Category>() {
val name: SimpleStringProperty = bind(Category::name)
val description: SimpleStringProperty = bind(Category::description)
}
class DBController : Controller() {
val categories: ObservableList<CategoryModel> by lazy {
transaction {
SchemaUtils.create(Categories)
Category.all().map {
CategoryModel().apply {
item = it
}
}.observable()
}
}
init {
Database.connect(
"jdbc:mysql://localhost:3306/test", driver = "com.mysql.cj.jdbc.Driver",
user = "test", password = "test"
)
TransactionManager.manager.defaultIsolationLevel = Connection.TRANSACTION_SERIALIZABLE
}
fun deleteCategory(model: CategoryModel) {
runAsync {
transaction {
model.item.delete()
}
}
categories.remove(model)
}
fun updateCategory(model: CategoryModel) {
transaction {
Categories.update {
model.commit()
}
}
}
fun commitDirty(modelDirtyMappings: Sequence<ModelToDirtyState>) {
transaction {
modelDirtyMappings.filter { it.value.isDirty }.forEach {
it.key.commit()
println(it.key)// commit value to database
it.value.commit() // clear dirty state
}
}
}
Just to quickly comment on controller, delete method works as "intended" however the update one does not, it does not work in sense that after using delete item is remove both from database and tableview(underlying list) itself, and when i do update its not, now i know the reason, i call remove manually on both database and list, now for update perhaps i could do change listener, or maybe tornadofx can do this for me, i just cant set it up to do it. Following code will make things clearer i think.
class CategoryEditor : View("Categories") {
val categoryModel: CategoryModel by inject()
val dbController: DBController by inject()
var categoryTable: TableViewEditModel<CategoryModel> by singleAssign()
var categories: ObservableList<CategoryModel> by singleAssign()
override val root = borderpane {
categories = dbController.categories
center = vbox {
buttonbar {
button("Commit") {
action {
dbController.commitDirty(categoryTable.items.asSequence())
}
}
button("Roll;back") {
action {
categoryTable.rollback()
}
}
// This model only works when i use categorytable.tableview.selected item, if i use categoryModel, list gets updated but not the view itself
// Question #1 how to use just categoryModel variable without need to use categorytable.tableview.selecteditem
button("Delete ") {
action {
val model = categoryTable.tableView.selectedItem
when (model) {
null -> return#action
else -> dbController.deleteCategory(model)
}
}
}
//And here no matter what i did i could not make the view update
button("Update") {
action {
when (categoryModel) {
null -> return#action
else -> dbController.updateCategory(categoryModel)
}
categoryTable.tableView.refresh()
}
}
}
tableview<CategoryModel> {
categoryTable = editModel
items = categories
enableCellEditing()
enableDirtyTracking()
onUserSelect() {
//open a dialog
}
//DOES WORK
categoryModel.rebindOnChange(this) { selectedItem ->
item = selectedItem?.item ?: CategoryModel().item
}
// Question #2. why bindSelected does not work, and i have to do it like above
//DOES NOT WORK
// bindSelected(categoryModel)
//
column("Name", CategoryModel::name).makeEditable()
column("Description", CategoryModel::description).makeEditable()
}
}
right = form {
fieldset {
field("Name") {
textfield(categoryModel.name)
}
}
fieldset {
field("Description") {
textfield(categoryModel.description)
}
}
button("ADD CATEGORY") {
action {
dbController.addCategory(categoryModel.name.value, categoryModel.description.value)
}
}
}
}
}
I apologize for huge amount of code, also in last code snipped i left questions in form of comments where i fail to achive desired results.
I am sure i am not properly binding code, i just dont see why, also i sometimes use one variable to update data, my declared one "categoryModel" and sometimes i use tableview.selecteditem, it just seems hacky and i cant seem to grasp way.
Thank you!
This is a know error when using C# expressions in windows workflow. The article at https://learn.microsoft.com/en-us/dotnet/framework/windows-workflow-foundation/csharp-expressions#CodeWorkflows explains the reason and how to fix it. It all works fine for me in standard workflows, but as soon as I add a custom NativeActivity to the WF, I get that same error again !
Below the code of how I load the XAML workflow and the simple NativeActivity (which is the ONLY activity in the test workflow and inside that activity is a simple assign expression).
Loading and invoking WF via XAML:
`XamlXmlReaderSettings settings = new XamlXmlReaderSettings()
{
LocalAssembly = GetContextAssembly()
};
XamlReader reader = reader = ActivityXamlServices.CreateReader(new XamlXmlReader(fileURL, settings));
ActivityXamlServicesSettings serviceSettings = new ActivityXamlServicesSettings
{
CompileExpressions = true
};
var activity = ActivityXamlServices.Load(reader, serviceSettings);
WorkflowInvoker.Invoke(activity);`
Doing it in code throws same Exception:
Variable<string> foo = new Variable<string>
{
Name = "Foo"
};
Activity activity = new Sequence
{
Variables = { foo },
Activities =
{
new TimeExecuteUntilAborted
{
Activities =
{
new Assign<string>
{
To = new CSharpReference<string>("Foo"),
Value = new CSharpValue<string>("new Random().Next(1, 101).ToString()")
}
}
}
}
};
CompileExpressions(activity);//the method from the article mentioned above
WorkflowInvoker.Invoke(activity);
The Native Activity:
[Designer("System.Activities.Core.Presentation.SequenceDesigner, System.Activities.Core.Presentation")]
public sealed class TimeExecuteUntilAborted : NativeActivity
{
private Sequence innerSequence = new Sequence();
[Browsable(false)]
public Collection<Activity> Activities
{
get
{
return innerSequence.Activities;
}
}
[Browsable(false)]
public Collection<Variable> Variables
{
get
{
return innerSequence.Variables;
}
}
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.AddImplementationChild(innerSequence);
}
protected override void Execute(NativeActivityContext context)
{
context.ScheduleActivity(innerSequence);
}
}
Your TimeExecutedUntilAborted class seems to be the culprit. I was able to swap in one of my own template NativeActivities instead and your workflow executed fine with the expressions. I'm guessing that your class is causing an issue in the compiler method when it parses your code. I used this doc as an example for my NativeActivity: https://msdn.microsoft.com/en-us/library/system.activities.nativeactivity(v=vs.110).aspx.
Sizzle Finger's answer is no solution but pointed me into the right direction to simply check what is different. It came out that the simple call to the base class method was missing:
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
base.CacheMetadata(metadata); // !! This needs to be added
metadata.AddImplementationChild(innerSequence);
}
I am trying to create function which reads a object from realm and emit an empty observable if the object isn't found. The code below works to some degree because I can stop it with the debugger and see it hit the Observable.empty():
fun readFromRealm(id: String): Observable<Player> {
return realm.where(Player::class.java)
.equalTo("id", id)
.findFirstAsync()
.asObservable<Player>()
.filter { it.isLoaded }
.flatMap {
if (it.isValid)
Observable.just(it)
else
Observable.empty()
}
}
But when I try to use a switchIfEmpty on the Observable the code never emits defaultPlayer when it is not found in realm.
return readFromRealm(playerId)
.take(1)
.map{ // do something with emitted observable }
.switchIfEmpty(Observable.just(defaultPlayer)) // use this if no player found
The strange thing is that if I update the original method to include a first() prior to the flatMap :
fun readFromRealm(id: String): Observable<Player> {
return realm.where(Player::class.java)
.equalTo("id", id)
.findFirstAsync()
.asObservable<Player>()
.filter { it.isLoaded }
.first() // add first
.flatMap {
if (it.isValid)
Observable.just(it)
else
Observable.empty()
}
}
Everything starts working as expected, but I believe this version will kill auto updating because it will only capture the first result emitted after the filter.
I'm still trying to grok Realm and Rx so I'm probably doing something dumb.
EDIT: I have created a sample project which highlights the issue I'm seeing - https://github.com/donaldlittlepie/realm-async-issue
For reasons I don't totally understand. If you move take(1) just above the
flatMap and below the filter it should work correctly:
realm.where(Dog.class)
.equalTo("id", 0L)
.findFirstAsync()
.asObservable()
.cast(Dog.class)
.filter(new Func1<RealmObject, Boolean>() {
#Override
public Boolean call(RealmObject realmObject) {
return realmObject.isLoaded();
}
})
.take(1) // <== here
.flatMap(new Func1<Dog, Observable<Dog>>() {
#Override
public Observable<Dog> call(Dog realmObject) {
if (realmObject.isValid()) {
return Observable.just(realmObject);
} else {
return Observable.empty();
}
}
})
.map(new Func1<Dog, Dog>() {
#Override
public Dog call(Dog dog) {
dog.setName("mapped " + dog.getName());
return dog;
}
})
.switchIfEmpty(Observable.just(createDefaultDog()))
.subscribe(new Action1<Dog>() {
#Override
public void call(Dog dog) {
textView.setText(dog.getName());
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
textView.setText(throwable.toString());
}
});
My best guess is that before, flatMap was called repeatedly, returning Observable.empty() multiple times. Perhaps that effects the Observable chain in some unexpected way.
Hi I have a WebFlowTestCase and is working fine but I ran into a prob when I need to test another flow that is in the same controller ( groovy).
The is what my controller looks like:
class MyController {
def someService
def dateHelper = new DateHelper()
def index = {... }
def myCreateFlow = {
start{}
createCase{}
finishCancel{
}
def myViewFlow = {...}
def myEditFlow = {...}
}
I have managed to successfully create the test for myCreateFlow like this:
class MyControllerTest extends WebFlowTestCase {
def myController = new MyController();
#Override
public Object getFlow() {
// TODO Auto-generated method stub
return myController.myCreateFlow
}
protected void setUp() {
super.setUp()
}
protected void tearDown() {
super.tearDown()
}
void testmyCreateFlow()
{
...
}
}
my question is how about the myEditFlow and myViewFlow? How do I register or use it when the getFlow() returns only the myCreateFlow? Is there I way I can use all of them in one webflowtest with out creating a new webflowtestclass? Or is there a way I can put it inside getflow with some switch/if else method something like:
#Override
public Object getFlow() {
// TODO Auto-generated method stub
if condition
return myController.myCreateFlow
else return myController.myEditFlow
}
coz when i tried creating a testmyEditFlow() I get the error below and I know that it is because the get flow only returns the myCreateFlow. At least that is how I perceive the test error msg.
Cannot find state with id 'myEditFlow' in flow 'test' -- Known state
ids are 'array['start', 'createCase'... 'finishCancel']'
You could register your other flows in a setUp method as follows:
protected void setUp() {
super.setUp()
registerFlow("myController/myEdit", myController.myEditFlow)
registerFlow("myController/myView", myController.myViewFlow)
}