atrium v0.12.0 Release Notes

Release Date: 2020-05-26 // almost 4 years ago
  • API Maturity: Stable
    Implementation Maturity: Almost Stable

    ๐Ÿ— There won't be any breaking changes in the API (assertion functions/builders) until v1.0.0 besides parameter name renaming and experimental features. But we want to progress as well and deprecate functionality in each version (e.g quite a lot with 0.7.0; please replace deprecated functionality until v1.0.0 where we will remove it).
    โœ… However, we do not provide yet a stable API for the domain and core modules of Atrium -- it is almost stable, but there might be slight breaking changes which we want to introduce before v1.0.0. That is also the reason why we do not have yet established backward compatibility tests for domain/core. This might affect you if you write your own assertion functions. And it also affects you if you provide your own implementation for parts of Atrium.

    Table of Content

    ๐Ÿ†• New Features

    api-fluent-en_GB

    • #375 Path.hasSameTextualContentAs => @thanks to tkech17

    ๐Ÿ†• new api-infix-en_GB

    Thanks to the following contributors to support the migration from cc-infix-en_GB to infix-en_GB:

    kotlin 1.3 extensions

    Domain / Core

    • none this time

    ๐Ÿ›  Fixes

    • none this time

    ๐Ÿ‘Œ Improvements

    • ๐Ÿ #235 setup code coverage for windows=> thanks to @ivanmiklec
    • ๐Ÿ— #245 Build samples as composite build => thanks to @assaflei
      #/167 compile android class files into dex bytecode => thanks to @assaflei
    • ๐Ÿ”€ #493 merge jdk8 extension into jvm module => thanks to @assaflei
    • #470 create maven sample project => thanks to @binkley
    • #466 fail if a TranslatableWithArgs has not exact arguments => thanks to @tkech17
    • #495 cache gradle and dependencies in github actions => thanks to @assaflei
    • ๐Ÿšš #506 remove android jars -> they are no longer required => thanks to @ultraon for checking if the module-info.class bug was really resolved in d8

    ๐Ÿ’ฅ Breaking Changes

    Planned (previously deprecated or announced)

    • none this time (yet we no longer publish a dedicated -android jar you can use the regular jvm artifact instead)

    Unplanned

    • none this time

    ๐Ÿ—„ Deprecation

    ๐Ÿšš The following was deprecated and will be removed with 1.0.0:

    • API cc-infix-en_GB => use infix-en_GB, fluent-en_GB respectively => a big thank you to @Miftahunajat for the many ReplaceWith he placed all over api-cc-infix to ease the migration ๐Ÿ‘
    • the jdk8 extension was merged into the normal module => search import ch.tutteli.atrium.api.fluent.en_GB.jdk8 replace with import ch.tutteli.atrium.api.fluent.en_GB

    ๐Ÿ’ฅ Breaking Changes with 1.0.0

    ๐Ÿ‘€ See atrium-roadmap -> Milestone 1.0.0

    ๐Ÿ—„ Migrating deprecated functionality

    API fluent

    โšก๏ธ There aren't any deprecations in api-fluent-en_GB in this version and thus also no migration required if you update from 0.9.x, 0.10.0 or 0.11.0

    ๐Ÿ”€ In case you used the jdk8 extension: it was merged into the main artifact:

    • perform a search for import ch.tutteli.atrium.api.fluent.en_GB.jdk8 and replace with import ch.tutteli.atrium.api.fluent.en_GB
    • โœ‚ remove the corresponding dependency

    API infix

    You should migrate from api-cc-infix-en_GB to the new api-infix-en_GB (api-cc-infix-en_GB will be removed with 1.0.0)
    ๐Ÿš€ In case you migrate from a version < 0.7.0 to this version, then please have a look at the migration guide given in the Release notes of v0.7.0 and v0.8.0 first.

    Otherwise you can use the suggested replacements (ALT + Enter -> Replace with ...) or the search/replace patterns shown below (recommended, since faster).

    Notice, that you don't have to migrate everything at once where asExpect and asAssert allow to switch between the old Assert and the new Expect world.
    Ping us in the Atrium slack channel if you need help.

    The following command is carrying out the points 1 to 14 as described below. The script for 15a or 15b follows afterwards (don't forget to do the manual points 16, 17, ...), run it from the root of your project, no guarantees that your system is capable of carrying it out. If not, you can use the manual steps described below.

    find ./ -path "*/test/*" -name "*.kt" | xargs perl -0777 -i \
    -pe 's/AssertImpl([\n\r\s]*)\.changeSubject\(([^\)\n]+)\)[\n\r\s]*\{[\n\r\s]*subject/ExpectImpl$1.changeSubject\($2\)$1.unreported \{ it/g;' \
    -pe 's/AssertImpl([\n\r\s]*)\.changeSubject\(([^\)]+)\)/ExpectImpl$1.changeSubject\($2\).unreported/g;' \
    -pe 's/AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.createDescriptive\(([^,\n]+,[^\)]+\)[\n\r\s]*\{[\n\r\s]*)plant.subject/AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.createDescriptive\(([^,\n]+,[^\)]+\)[\n\r\s]*\{[\n\r\s]*)plant.subject/g;' \
    -pe 's/AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.descriptive([\n\r\s]*).withTest(\(?[\n\r\s]*)\{([\n\r\s]*)plant.subject/AssertImpl$1.builder$2.descriptive$3.withTest\(plant\)$4\{$5it/g;' \
    -pe 's/AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.descriptive([\n\r\s]*).withTest(\(?[\n\r\s]*)\{([\n\r\s]*)subject/AssertImpl$1.builder$2.descriptive$3.withTest\(this\)$4\{$5it/g;' \
    -pe 's/(\.| )((?:toThrow|isA)<.*>)\s*\{\s*\}/$1$2()/g;' \
    -pe 's/notToBeNull\s*\{\s*\}/notToBe null/g;' \
    -pe 's/fun <T\s*:\s*Any> ([^\(]+)\(subject:\s*T\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlant\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[\n\r\s]*reporter\)/fun <T> $1\(subject: T\): Expect<T> = \n ExpectBuilder.forSubject\(subject\)\n .withVerb\($2\)\n .withoutOptions\(\)\n .build\(\)/g;' \
    -pe 's/fun <T\s*:\s*Any> ([^\(]+)\(subject:\s*T\s*,[\n\r\s]*assertionCreator: Assert<T>.\(\)\s*->\s*Unit\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlantAndAddAssertionsCreatedBy\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[\n\r\s]*reporter,[\n\r\s]*assertionCreator\)/fun <T> $1\(subject: T, assertionCreator: Expect<T>.\(\) -> Unit\): Expect<T> = \n $1(subject).addAssertionsCreatedBy(assertionCreator)/g;' \
    -pe 's/(?:internal )?fun <T(?:\s*:\s*Any\?)?> ([^\(]+)\(subject:\s*T\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlantNullable\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[^\)]+\)//g;' \
    -pe 's/import ch.tutteli.atrium.verbs\.(expect|assert|assertThat)/import ch.tutteli.atrium.api.verbs.$1/g;' \
    -pe 's/AssertImpl/ExpectImpl/g;' \
    -pe 's/fun Assert(?:ionPlant(?:Nullable)?)?<(.*)>\./fun <T: $1> Expect<T>\./g;' \
    -pe 's/Assert(ionPlant(Nullable)?)?</Expect</g;' \
    -pe 's/import ch\.tutteli\.atrium\.creating\.Assert(ionPlant(Nullable)?)?/import ch.tutteli.atrium.creating.Expect/g;' \
    -pe 's/import ch.tutteli\.atrium\.api\.cc\.infix\.en_GB/import ch.tutteli.atrium.api.infix.en_GB/g;' \
    -pe 's/is(Less|Greater)OrEquals/is$1ThanOrEqual/g;' \
    -pe 's/\.asIterable\(\)/ asList o/g;' \
    -pe 's/asIterable(\s*\{)/asList$1/g;' \
    -pe 's/\.asEntries\(\)/ asEntries o/g;' \
    -pe 's/ o / it /g;' \
    -pe 's/get\s*Index\(([^\)]+)\)\s*assertIt\s*\{([^\}]+)\}/get index($1) {$2}/g;' \
    -pe 's/import ch.tutteli.atrium.api.infix.en_GB.Index/import ch.tutteli.atrium.api.infix.en_GB.index/g;' \
    -pe 's/getExisting\s*Key\(([^\)]+)\)\s*assertIt\s*\{([^\}]+)\}/getExisting key($1) {$2}/g;' \
    -pe 's/import ch.tutteli.atrium.api.infix.en_GB.Key/import ch.tutteli.atrium.api.infix.en_GB.key/g;' 
    

    In case you use Kotlin 1.4 or have enabled the new type inference, then you can use the following script to carry out point 13a:

    find ./ -path "*/test/*" -name "*.kt" | xargs perl -0777 -i \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)/$1its feature of(\{ f(it::$2) \})$3/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)/feature of(\{ f(it::$1) \})$2/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)/$1its feature \{ f(it::$2) \}/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)/feature \{ f(it::$1) \}/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)(\s*\{)/$1its feature of($2::$3)$4/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)(\s*\{)/feature of($1::$2)$3/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\),]+)\)/$1 its feature($2::$3)/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\),]+)\)/feature($1::$2)/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)/$1its feature of($2::$3)/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)/feature of($1::$2)/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)/$1its feature of("$2.$3") { $2.$3 }/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)/feature("$1.$2", { $1.$2 })/g;' \
    -pe 's/(import ch\.tutteli\.atrium\.api\.cc\.(?:\.infix)?(?:en_GB|de_CH))\.(property|returnValueOf)/$1.feature/g;' \
    

    Otherwise you need to carry out the point 13b which can be done with the following script`

    find ./ -path "*/test/*" -name "*.kt" | xargs perl -0777 -i \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)/$1its feature \{ f(it::$2) \} it$3/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)/feature \{ f(it::$1) \} it$2/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)/$1its feature \{ f(it::$2) \}/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)/feature \{ f(it::$1) \}/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)(\s*\{)/$1its feature of($2::$3)$4/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)(\s*\{)/feature of($1::$2)$3/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\),]+)\)/$1 its feature($2::$3)/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\),]+)\)/feature($1::$2)/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)/$1its feature of($2::$3)/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)/feature of($1::$2)/g;' \
    -pe 's/((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)/$1its feature of("$2.$3") { $2.$3 }/g;' \
    -pe 's/(?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)/feature("$1.$2", { $1.$2 })/g;' \
    -pe 's/(import ch\.tutteli\.atrium\.api\.cc\.(?:\.infix)?(?:en_GB|de_CH))\.(property|returnValueOf)/$1.feature/g;'
    

    โ†ช But you still need to add manually import ch.tutteli.atrium.api.infix.en_GB.workaround.it where it is not known


    In case you cannot carry out the commands above, then read on to perform the manual steps:

    The following list helps you to migrate faster by using a few regex search replace commands (in Intellij). Make sure you have checked Regex as well as Match Case in the search options. Notice, that the code will certainly not compile after a single replace, you need to carry out all search&replace commands.
    It is not perfect, maybe you need to do a few adjustments in addition, let us now and we improve the search/replace commands here.

    Switch to ExpectImpl.changeSubject instead of using AssertImpl.changeSubject:
    Search: AssertImpl([\n\r\s]*)\.changeSubject\(([^\)\n]+)\)[\n\r\s]*\{[\n\r\s]*subject
    Replace: ExpectImpl$1.changeSubject\($2\)$1.unreported { it

    Search: AssertImpl([\n\r\s]*)\.changeSubject\(([^\)]+)\)
    Replace: ExpectImpl$1.changeSubject\($2\).unreported

    ๐Ÿ— builder.descriptive, safe withTest

    ๐Ÿ— Search: AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.createDescriptive\(([^,\n]+,[^\)]+\)[\n\r\s]*\{[\n\r\s]*)plant.subject
    ๐Ÿ— Replace: AssertImpl$1.builder$2.createDescriptive\(plant, $3it

    ๐Ÿ— Search: AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.descriptive([\n\r\s]*).withTest(\(?[\n\r\s]*)\{([\n\r\s]*)plant.subject
    ๐Ÿ— Replace: AssertImpl$1.builder$2.descriptive$3.withTest\(plant\)$4{$5it

    ๐Ÿ— Search: AssertImpl([\n\r\s]*)\.builder([\n\r\s]*)\.descriptive([\n\r\s]*).withTest(\(?[\n\r\s]*)\{([\n\r\s]*)subject
    ๐Ÿ— Replace: AssertImpl$1.builder$2.descriptive$3.withTest\(this\)$4{$5it

    toThrow and isA with empty assertionCreator lambda
    Search: (\.| )((?:toThrow|wirft|isA|istEin)<.*>)\s*\{\s*\}
    Replace $1$2()

    notToBeNull with empty assertionCreator lambda
    Search: notToBeNull\s*\{\s*\}
    Replace: notToBe null

    migrate custom assertion verbs:

    Search: fun <T\s*:\s*Any> ([^\(]+)\(subject:\s*T\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlant\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[\n\r\s]*reporter\)
    Replace:
    ๐Ÿ— fun <T> $1\(subject: T\): Expect<T> = \n ExpectBuilder.forSubject\(subject\)\n .withVerb\($2\)\n .withoutOptions\(\)\n .build\(\)

    Search:
    fun <T\s*:\s*Any> ([^\(]+)\(subject:\s*T\s*,[\n\r\s]*assertionCreator: Assert<T>.\(\)\s*->\s*Unit\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlantAndAddAssertionsCreatedBy\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[\n\r\s]*reporter,[\n\r\s]*assertionCreator\)
    Replace:
    fun <T> $1\(subject: T, assertionCreator: Expect<T>.\(\) -> Unit\): Expect<T> = \n $1(subject).addAssertionsCreatedBy(assertionCreator)

    Search:
    (?:internal )?fun <T(?:\s*:\s*Any\?)?> ([^\(]+)\(subject:\s*T\)[\n\r\s]*=[\n\r\s]*AssertImpl[\n\r\s]*\.coreFactory[\n\r\s]*\.newReportingPlantNullable\(([^,]+),[\n\r\s]*\{\s*subject\s*\}[\n\r\s]*,[^\)]+\)
    Replace: (empty string)

    In case the above search&replace did not find anything (because your code is different):
    ๐Ÿ”จ Switch from AssertImpl.coreFactory.newReportingPlant to ExpectBuilder
    ๐Ÿ— => see atriumVerbs.kt for an example of how own assertion verbs are defined now; or use the suggested replacements but please add import ch.tutteli.atrium.domain.builders.reporting.ExpectBuilder first as it will not work correctly otherwise due to an Intellij bug
    => Note that you don't need a verb for nullable types any more. Thus:

    • remove the upper bound T: Any
    • remove the verb which uses `newReportingPlantNullable
    • remove the verb which expected act: () -> Unit

    Switch to new built-in assertion verbs which use Expect

    Search: import ch.tutteli.atrium.verbs.(expect|assert|assertThat)
    Replace: import ch.tutteli.atrium.api.verbs.$1

    Switch from AssertImpl to ExpectImpl

    Search: AssertImpl
    Replace: ExpectImpl

    Switch all your assertion functions to use Expect and no longer Assert:

    Search: import ch\.tutteli\.atrium\.creating\.Assert(ionPlant(Nullable)?)?
    Replace: import ch.tutteli.atrium.creating.Expect

    Search: fun Assert(?:ionPlant(?:Nullable)?)?<(.*)>\.
    Replace: fun <T: $1> Expect<T>\.

    Search: Assert(ionPlant(Nullable)?)?<
    Replace: Expect<

    Switch the API

    Search: import ch.tutteli\.atrium\.api\.cc\.infix\.en_GB Replace:import ch.tutteli.atrium.api.infix`

    isLessOr/isGreaterOrEquals

    Search: is(Less|Greater)OrEquals
    Replace: is$1ThanOrEqual

    11 use asList instead of asIterable and replace asEntries()

    Search: \.asIterable\(\)
    Replace: asList o

    Search: asIterable(\s*\{)
    Replace: asList$1

    Search: asEntries\(\)
    Replace: asEntries o

    12 List get Index assertIt
    This one has to be done with care as there could be nested assertion group blocks

    Search: get\s*Index\(([^\)]+)\)\s*assertIt\s*\{([^\}]+)\}
    Replace: get index($1) {$2}

    Search: import ch.tutteli.atrium.api.infix.en_GB.Index
    Replace: import ch.tutteli.atrium.api.infix.en_GB.index

    13 Map getExisting Key assertIt
    This one has to be done with care as there could be nested assertion group blocks

    Search: getExisting\s*Key\(([^\)]+)\)\s*assertIt\s*\{([^\}]+)\}
    Replace: getExisting key($1) {$2}

    Search: import ch.tutteli.atrium.api.infix.en_GB.Key
    Replace: import ch.tutteli.atrium.api.infix.en_GB.key

    14 use it instead of o inside assertion groups:
    Search: o
    Replace: it (you can also replace by its)

    15a. use new feature mechanism - replacements for Kotlin 1.4 or the new inference (skip to 13b in case you use Kotlin < 1.4)

    This one needs extra care as arguments could be function calls. Verify the replacements

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)
    Replace: $1its feature of(\{ f(it::$2) \})$3

    Search: (?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)
    Replace: feature of(\{ f(it::$1) \})$2

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)
    Replace: $1its feature \{ f(it::$2) \}

    Search: (?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)
    Replace: feature \{ f(it::$1) \}

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)(\s*\{)
    Replace: $1its feature of($2::$3)$4

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)(\s*\{)
    Replace: feature of($1::$2)$3

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\),]+)\)
    Replace: $1 its feature($2::$3)

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\),]+)\)
    Replace: feature($1::$2)

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)
    Replace: $1its feature of($2::$3)

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)
    Replace: feature of($1::$2)

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)
    Replace: $1its feature of("$2.$3") { $2.$3 }

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)
    Replace: feature("$1.$2", { $1.$2 })

    Search: (import ch\.tutteli\.atrium\.api\.cc\.(?:\.infix)?(?:en_GB|de_CH))\.(property|returnValueOf)
    Replace: $1.feature

    15b use new feature mechanism - replacements for Kotlin < 1.4 (skip if you already applied 11a)

    This one needs extra care as arguments could be function calls. Verify the replacements

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)
    โž• add import ch.tutteli.atrium.api.infix.en_GB.workaround.it to those files

    Search again for: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)
    Replace: $1its feature \{ f(it::$2) \} it$3

    Search: (?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)(\s*\{)
    Replace: feature \{ f(it::$1) \} it$2

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)
    Replace: $1its feature \{ f(it::$2) \}

    Search: (?:property|returnValueOf|rueckgabewertVon)\((?:[\n\r\s]*)subject::([^\)]+)\)
    Replace: feature \{ f(it::$1) \}

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)(\s*\{)
    Replace: $1its feature of($2::$3)$4

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)(\s*\{)
    Replace: feature of($1::$2)$3

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\),]+)\)
    Replace: $1 its feature($2::$3)

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\),]+)\)
    Replace: feature($1::$2)

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)
    Replace: $1its feature of($2::$3)

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([A-Z][^:]+)::([^\)]+)\)
    Replace: feature of($1::$2)

    Search: ((?:[\n\r]+|\{)\s*)(?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)
    Replace: $1its feature of("$2.$3") { $2.$3 }

    Search: (?:property|returnValueOf|rueckgabewertVon)\(([^:]+)::([^\)]+)\)
    Replace: feature("$1.$2", { $1.$2 })

    Search: (import ch\.tutteli\.atrium\.api\.cc\.(?:\.infix)?(?:en_GB|de_CH))\.(property|returnValueOf)
    Replace: $1.feature

    โ†ช But you still need to add manually import ch.tutteli.atrium.api.infix.en_GB.workaround.it where it is not known

    16 In case you have custom assertion verbs
    Dealing with thrown exceptions is now handled by Expect as well.
    ๐Ÿ‘ป However, in case you have named the assertion verb differently for expecting an Exception then you have to decide:

    • ๐Ÿšš use the same name => rename the corresponding function which expects act: () -> Unit to the same name and remove it afterwards
    • ๐Ÿ‘‰ use a different name => delegate the function which expects act: () -> Unit to the other verb

    ๐Ÿ— Check if you need to add import ch.tutteli.atrium.domain.builders.reporting.ExpectBuilder

    Try to reduce duplicated Expect imports
    Repeat until you don't have duplicate imports any more
    Search: import ch\.tutteli\.atrium\.creating\.Expect\n\s*import ch\.tutteli\.atrium\.creating\.Expect
    Replace: import ch.tutteli.atrium.creating.Expect

    โš  Try to compile your project and watch out for the following warnings:

    • 'MyClass' is a final type, and thus a value of the type parameter is predetermined
      => you can suppress this warning by adding @file:Suppress("FINAL_UPPER_BOUND") to your file, this is actually a Kotlin bug (https://youtrack.jetbrains.com/issue/KT-34257)