From 7e856b1d28cefd78d06a493fec9d979b850b35d9 Mon Sep 17 00:00:00 2001 From: Nikita Date: Sun, 18 Feb 2024 14:32:19 +0300 Subject: [PATCH 01/10] Closes #188 --- .../core/reflection/SelectorReflectionFields.kt | 17 ++++++++++++----- .../reflection/SelectorReflectionFieldsTest.kt | 10 +++++++++- .../org/xpathqs/core/reflection/pages/pages.kt | 7 ++++++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/org/xpathqs/core/reflection/SelectorReflectionFields.kt b/src/main/kotlin/org/xpathqs/core/reflection/SelectorReflectionFields.kt index 5e4a346..ac56b14 100644 --- a/src/main/kotlin/org/xpathqs/core/reflection/SelectorReflectionFields.kt +++ b/src/main/kotlin/org/xpathqs/core/reflection/SelectorReflectionFields.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 XPATH-QS + * Copyright (c) 2024 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -45,13 +45,20 @@ class SelectorReflectionFields( * Returns collection of [BaseSelector]s inner objects of [rootObj] */ val innerSelectors: Collection by lazy { - innerSelectorProps.filter { - it.call(rootObj) !is NullSelector - }.map { - (it.call(rootObj) as BaseSelector).setName(it.name) + innerSelectorProps.mapNotNull { + (safeEvaluate(it) as? BaseSelector)?.setName(it.name) } } + private fun safeEvaluate(prop: KProperty<*>) : Any? { + return runCatching { + prop.call(rootObj) + }.getOrNull() ?: run { + runCatching { + prop.call() + }.getOrNull() + } + } /** * Returns collection of [Block]s inner objects of [rootObj] */ diff --git a/src/test/kotlin/org/xpathqs/core/reflection/SelectorReflectionFieldsTest.kt b/src/test/kotlin/org/xpathqs/core/reflection/SelectorReflectionFieldsTest.kt index c6c30b8..5e49d63 100644 --- a/src/test/kotlin/org/xpathqs/core/reflection/SelectorReflectionFieldsTest.kt +++ b/src/test/kotlin/org/xpathqs/core/reflection/SelectorReflectionFieldsTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 XPATH-QS + * Copyright (c) 2024 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -78,6 +78,14 @@ class SelectorReflectionFieldsTest : AnnotationSpec() { names shouldContainExactlyInAnyOrder listOf("s1", "originBlock") } + @Test + fun innerSelectorPrivateField() { + val actual = SelectorReflectionFields(PagePrivateMember).innerSelectors + val names = actual.map { it.name } + + names shouldContainExactlyInAnyOrder listOf("s1") + } + @Test fun innerSelectorFieldsWithInnerObject() { val actual = SelectorReflectionFields(PageWithBaseAndInnerObject).innerSelectorProps diff --git a/src/test/kotlin/org/xpathqs/core/reflection/pages/pages.kt b/src/test/kotlin/org/xpathqs/core/reflection/pages/pages.kt index 8e01cee..1312839 100644 --- a/src/test/kotlin/org/xpathqs/core/reflection/pages/pages.kt +++ b/src/test/kotlin/org/xpathqs/core/reflection/pages/pages.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 XPATH-QS + * Copyright (c) 2024 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,6 +29,7 @@ import org.xpathqs.core.selector.extensions.plus import org.xpathqs.core.selector.extensions.repeat import org.xpathqs.core.selector.selector.Selector import org.xpathqs.core.selector.selector.SelectorProps +import org.xpathqs.core.util.SelectorFactory.attrSelector import org.xpathqs.core.util.SelectorFactory.tagSelector open class Block2 : Block() @@ -55,6 +56,10 @@ object PageNoBase : Block() { val s1 = Selector(props = SelectorProps(tag = "s1")) } +object PagePrivateMember : Block() { + private val s1 = attrSelector("s", "v") +} + object PageWithBaseWithChainXpath : Block(tagSelector("div").repeat(3)) { val s1 = Selector(props = SelectorProps(tag = "s1")) } From f7994898da4b1aa8940664798407781ac189d1b9 Mon Sep 17 00:00:00 2001 From: Nikita Date: Sun, 18 Feb 2024 15:05:46 +0300 Subject: [PATCH 02/10] Closes #188 --- .github/workflows/build.yml | 17 +++++------------ build.gradle.kts | 6 +++++- detekt.yml | 1 + readme.md | 3 +-- .../core/reflection/SelectorReflection.kt | 9 ++------- 5 files changed, 14 insertions(+), 22 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 97b4e4b..cd12e74 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,15 +62,8 @@ jobs: echo "coverage = ${{ steps.jacoco.outputs.coverage }}" echo "branch coverage = ${{ steps.jacoco.outputs.branches }}" - - name: Add new badge - uses: EndBug/add-and-commit@v7 - with: - default_author: github_actions - message: 'upload badge' - add: '*.svg' - - - name: Upload JaCoCo coverage report - uses: actions/upload-artifact@v2 - with: - name: jacoco-report - path: build/reports/jacoco/ \ No newline at end of file + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v4.0.1 + env: + token: ${{ secrets.CODECOV_TOKEN }} + slug: xpathqs/core \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index c658c65..490fbc0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,7 +35,7 @@ plugins { `maven-publish` signing id("io.codearte.nexus-staging") version "0.30.0" - id("io.gitlab.arturbosch.detekt").version("1.22.0") + id("io.gitlab.arturbosch.detekt").version("1.23.5") id("info.solidsoft.pitest").version("1.9.0") id("org.jetbrains.kotlinx.kover") version "0.6.1" } @@ -202,3 +202,7 @@ pitest { testStrengthThreshold.set(50) coverageThreshold.set(50) } + +detekt { + +} diff --git a/detekt.yml b/detekt.yml index 0e75ce1..3ce5856 100644 --- a/detekt.yml +++ b/detekt.yml @@ -686,6 +686,7 @@ style: active: true UnusedPrivateMember: active: true + activeInTests: false allowedNames: '(_|ignored|expected|serialVersionUID)' UseAnyOrNoneInsteadOfFind: active: true diff --git a/readme.md b/readme.md index 620fa79..64b5a7e 100644 --- a/readme.md +++ b/readme.md @@ -1,8 +1,7 @@ [![CI/CD](https://github.com/nachg/xpathqs-core/actions/workflows/build.yml/badge.svg)](https://github.com/nachg/xpathqs-core/actions/workflows/build.yml) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.xpathqs/core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.xpathqs/core/) ![GitHub top language](https://img.shields.io/github/languages/top/nachg/xpathqs-core) -[![Coverage](.github/badges/jacoco.svg)](jacoco.svg) - +[![codecov](https://codecov.io/gh/xpathqs/core/graph/badge.svg?token=3YVDC15O9T)](https://codecov.io/gh/xpathqs/core) [![License](https://img.shields.io/github/license/nachg/xpathqs-core)](https://github.com/nachg/xpathqs-core/blob/master/LICENSE) [![EO principles respected here](https://www.elegantobjects.org/badge.svg)](https://www.elegantobjects.org) diff --git a/src/main/kotlin/org/xpathqs/core/reflection/SelectorReflection.kt b/src/main/kotlin/org/xpathqs/core/reflection/SelectorReflection.kt index 450c2e8..a4a3962 100644 --- a/src/main/kotlin/org/xpathqs/core/reflection/SelectorReflection.kt +++ b/src/main/kotlin/org/xpathqs/core/reflection/SelectorReflection.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 XPATH-QS + * Copyright (c) 2024 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -58,16 +58,11 @@ internal class SelectorReflection( } fun setProp(name: String, value: Any?): SelectorReflection { - val member = findField(name) - - if (member != null) { + findField(name).let { member -> member.isAccessible = true //Kotlin can't cast KProperty to KMutableProperty member.set(obj, value) - } else { - throw ReflectionException.NoSuchField() } - return this } From 11f25b599eb39d720fb9dc9fb5a9990c982dcea1 Mon Sep 17 00:00:00 2001 From: Nikita Date: Sun, 18 Feb 2024 18:07:03 +0300 Subject: [PATCH 03/10] Closes #188 --- build.gradle.kts | 4 ++-- detekt.yml | 6 +++--- .../org/xpathqs/core/model/ModelAssociation.kt | 5 +++-- .../core/reflection/ReflectionExtensions.kt | 4 ++-- .../org/xpathqs/core/reflection/SelectorParser.kt | 12 ++++++------ .../xpathqs/core/reflection/SelectorReflection.kt | 2 +- .../core/selector/base/AnnotationExtensions.kt | 15 ++++++++------- .../xpathqs/core/selector/base/CoreExtensions.kt | 9 ++++----- .../selector/block/BlockSelectorCoreExtensions.kt | 6 +++--- .../org/xpathqs/core/util/PropertyFacadeTest.kt | 8 +++----- 10 files changed, 35 insertions(+), 36 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 490fbc0..cbed446 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -28,7 +28,7 @@ group = "org.xpathqs" val kotestVersion = "5.8.0" plugins { - kotlin("jvm") version "1.9.22" + kotlin("jvm") version "2.0.0-Beta4" id("org.jetbrains.dokka") version "1.4.32" `java-library` jacoco @@ -62,7 +62,7 @@ detekt { dependencies { implementation("io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.30.0") - implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.22") + implementation("org.jetbrains.kotlin:kotlin-reflect:2.0.0-Beta4") implementation("org.yaml:snakeyaml:1.28") implementation("net.oneandone.reflections8:reflections8:0.11.7") diff --git a/detekt.yml b/detekt.yml index 3ce5856..cb0b3a6 100644 --- a/detekt.yml +++ b/detekt.yml @@ -485,8 +485,9 @@ potential-bugs: active: true UnreachableCatchBlock: active: true + # UnreachableCode is turned off UnreachableCode: - active: true + active: false UnsafeCallOnNullableType: active: true excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] @@ -685,8 +686,7 @@ style: UnusedPrivateClass: active: true UnusedPrivateMember: - active: true - activeInTests: false + active: false allowedNames: '(_|ignored|expected|serialVersionUID)' UseAnyOrNoneInsteadOfFind: active: true diff --git a/src/main/kotlin/org/xpathqs/core/model/ModelAssociation.kt b/src/main/kotlin/org/xpathqs/core/model/ModelAssociation.kt index bda4cb3..20a5eb6 100644 --- a/src/main/kotlin/org/xpathqs/core/model/ModelAssociation.kt +++ b/src/main/kotlin/org/xpathqs/core/model/ModelAssociation.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 XPATH-QS + * Copyright (c) 2024 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,11 +29,12 @@ import kotlin.reflect.KProperty data class ModelAssociation( val sel: BaseSelector, - val field: Field + val field: Field? ) { constructor(sel: BaseSelector, prop: KProperty<*>) : this(sel, prop.toField()) fun applyTo(source: Any, extractor: ISelectorValueExtractor) { + requireNotNull(field) field.set(source, extractor.apply(this)) } } \ No newline at end of file diff --git a/src/main/kotlin/org/xpathqs/core/reflection/ReflectionExtensions.kt b/src/main/kotlin/org/xpathqs/core/reflection/ReflectionExtensions.kt index 133d320..6ada55e 100644 --- a/src/main/kotlin/org/xpathqs/core/reflection/ReflectionExtensions.kt +++ b/src/main/kotlin/org/xpathqs/core/reflection/ReflectionExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 XPATH-QS + * Copyright (c) 2024 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -194,7 +194,7 @@ internal fun Block.setBlank(value: Boolean) { /** * Converts Kotlin Reflection property to the Java Reflection field */ -internal fun KProperty<*>.toField() = this.javaField!!.apply { +internal fun KProperty<*>.toField() = this.javaField?.apply { isAccessible = true } diff --git a/src/main/kotlin/org/xpathqs/core/reflection/SelectorParser.kt b/src/main/kotlin/org/xpathqs/core/reflection/SelectorParser.kt index f8e9cd9..fe626b6 100644 --- a/src/main/kotlin/org/xpathqs/core/reflection/SelectorParser.kt +++ b/src/main/kotlin/org/xpathqs/core/reflection/SelectorParser.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 XPATH-QS + * Copyright (c) 2024 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -68,7 +68,7 @@ class SelectorParser( */ fun parse() { val baseName = if (base.name.isNotEmpty()) base.name + "." else "" - val rootAnn = rootObj::class.findAnnotation()?.value ?: rootObj::class.simpleName!! + val rootAnn = rootObj::class.findAnnotation()?.value ?: rootObj::class.simpleName val rootName = rootObj.name.ifEmpty { baseName + rootAnn} val rootFullName = rootObj.fullName.ifEmpty { rootObj.javaClass.`package`.name + "." + baseName + rootAnn @@ -135,9 +135,9 @@ class SelectorParser( annotations: Collection ) { if(base !is NullSelector) { - val isNoBase = annotations.firstOrNull { + val isNoBase = annotations.any { it.annotationClass.java == NoBase::class.java - } != null + } if(!isNoBase) { val notFreeze = (to.base as? BaseSelector)?.state != SelectorState.FREEZE @@ -166,9 +166,9 @@ class SelectorParser( } } - val noXpBase = annotations.firstOrNull { + val noXpBase = annotations.any { it.annotationClass.java == NoXpathBase::class.java - } != null + } if(noXpBase) { to.setNoBase(true) diff --git a/src/main/kotlin/org/xpathqs/core/reflection/SelectorReflection.kt b/src/main/kotlin/org/xpathqs/core/reflection/SelectorReflection.kt index a4a3962..4920661 100644 --- a/src/main/kotlin/org/xpathqs/core/reflection/SelectorReflection.kt +++ b/src/main/kotlin/org/xpathqs/core/reflection/SelectorReflection.kt @@ -89,7 +89,7 @@ internal class SelectorReflection( /** * Set
props.args
field of the [obj] */ - fun setArgs(args: SelectorArgs) = BaseSelectorProps::args.toField().set(obj.props, args) + fun setArgs(args: SelectorArgs) = BaseSelectorProps::args.toField()?.set(obj.props, args) /** * Mark [obj] as freeze diff --git a/src/main/kotlin/org/xpathqs/core/selector/base/AnnotationExtensions.kt b/src/main/kotlin/org/xpathqs/core/selector/base/AnnotationExtensions.kt index 4474d54..c7e6c52 100644 --- a/src/main/kotlin/org/xpathqs/core/selector/base/AnnotationExtensions.kt +++ b/src/main/kotlin/org/xpathqs/core/selector/base/AnnotationExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2024 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,10 +30,9 @@ import kotlin.reflect.KClass * returns true if provided annotation is present * in the selector's annotation list */ -fun BaseSelector.hasAnnotation(annotation: KClass<*>) - = annotations.find { +fun BaseSelector.hasAnnotation(annotation: KClass<*>) = annotations.any { it.annotationClass.qualifiedName?.removeSuffix(".Container") == annotation.qualifiedName -} != null +} /** * returns true if provided annotation is present @@ -58,8 +57,9 @@ fun BaseSelector.hasParentAnnotation(annotation: KClass<*>) * Require #2 - when parent [org.xpathqs.core.selector.block.Block] doesn't has provided annotation - return false * @sample [org.xpathqs.core.selector.base.BaseSelectorAnnotationsTest.r2_hasParentAnnotation] */ -fun BaseSelector.hasAnyParentAnnotation(annotation: KClass<*>) - = this.parents.find { it.hasAnnotation(annotation) } != null +fun BaseSelector.hasAnyParentAnnotation(annotation: KClass<*>) = this.parents.any { + it.hasAnnotation(annotation) + } /** * @return provided annotation object @@ -76,10 +76,11 @@ inline fun BaseSelector.findAnnotation(): T? { } as? T } +@Suppress("UNCHECKED_CAST") inline fun BaseSelector.findAnnotations(): Collection { return annotations.filter { it.annotationClass.qualifiedName?.removeSuffix(".Container") == T::class.qualifiedName - } as Collection + } as List } /** * @return provided annotation object of parent [org.xpathqs.core.selector.block.Block] diff --git a/src/main/kotlin/org/xpathqs/core/selector/base/CoreExtensions.kt b/src/main/kotlin/org/xpathqs/core/selector/base/CoreExtensions.kt index ac7bf62..8748430 100644 --- a/src/main/kotlin/org/xpathqs/core/selector/base/CoreExtensions.kt +++ b/src/main/kotlin/org/xpathqs/core/selector/base/CoreExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 XPATH-QS + * Copyright (c) 2024 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -117,10 +117,9 @@ internal fun T.newInstance(): T { * @return constructor with provided parameterCount */ private fun Any.getConstructor(parameterCount: Int): Constructor<*> { - val c = this::class.java.declaredConstructors.find { + return this::class.java.declaredConstructors.firstOrNull { it.parameterCount == parameterCount + }?.apply { + isAccessible = true } ?: throw IllegalArgumentException("Selector doesn't have a default constructor with $parameterCount parameter") - - c.isAccessible = true - return c } \ No newline at end of file diff --git a/src/main/kotlin/org/xpathqs/core/selector/block/BlockSelectorCoreExtensions.kt b/src/main/kotlin/org/xpathqs/core/selector/block/BlockSelectorCoreExtensions.kt index e2c5525..9638ffc 100644 --- a/src/main/kotlin/org/xpathqs/core/selector/block/BlockSelectorCoreExtensions.kt +++ b/src/main/kotlin/org/xpathqs/core/selector/block/BlockSelectorCoreExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 XPATH-QS + * Copyright (c) 2024 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -136,8 +136,8 @@ val T.allInnerSelectors: Collection get() { val res = ArrayList() res.addAll(children) - selectorBlocks.forEach { - (it as? Block)?.allInnerSelectors?.let { + selectorBlocks.forEach { block -> + (block as? Block)?.allInnerSelectors?.let { res.addAll(it) } } diff --git a/src/test/kotlin/org/xpathqs/core/util/PropertyFacadeTest.kt b/src/test/kotlin/org/xpathqs/core/util/PropertyFacadeTest.kt index 3dfe44b..76e29e6 100644 --- a/src/test/kotlin/org/xpathqs/core/util/PropertyFacadeTest.kt +++ b/src/test/kotlin/org/xpathqs/core/util/PropertyFacadeTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 XPATH-QS + * Copyright (c) 2024 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -45,8 +45,7 @@ internal class PropertyFacadeTest : AnnotationSpec() { @Test fun parseWithInputStream() { CoreGlobalProps( - this::class.java.classLoader.getResource(path)?.openStream() - ?: throw IllegalArgumentException("'$path' Resource can't be found") + this::class.java.classLoader.getResource(path)!!.openStream() ).TEXT_ARG shouldBe "@text_test" } @@ -54,8 +53,7 @@ internal class PropertyFacadeTest : AnnotationSpec() { fun parseWithFacade() { CoreGlobalProps( PropertyFacade( - this::class.java.classLoader.getResource(path)?.openStream() - ?: throw IllegalArgumentException("'$path' Resource can't be found") + this::class.java.classLoader.getResource(path)!!.openStream() ) ).TEXT_ARG shouldBe "@text_test" } From 545ef7417ba993f7789a07fc6f526d5852519a8f Mon Sep 17 00:00:00 2001 From: Nikita Date: Sun, 18 Feb 2024 18:23:30 +0300 Subject: [PATCH 04/10] codecov fix --- .github/workflows/build.yml | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cd12e74..58bf0da 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,19 +51,8 @@ jobs: - name: Run Test Coverage run: ./gradlew jacocoTestReport - - name: Generate JaCoCo Badge - id: jacoco - uses: cicirello/jacoco-badge-generator@v2.1.0 - with: - jacoco-csv-file: build/reports/jacoco/test/jacocoTestReport.csv - - - name: Log coverage percentage - run: | - echo "coverage = ${{ steps.jacoco.outputs.coverage }}" - echo "branch coverage = ${{ steps.jacoco.outputs.branches }}" - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v4.0.1 - env: + uses: codecov/codecov-action@v4 + with: token: ${{ secrets.CODECOV_TOKEN }} slug: xpathqs/core \ No newline at end of file From c8be58ea2616bab96a46d7d392a55abddc771374 Mon Sep 17 00:00:00 2001 From: Nikita Date: Sun, 18 Feb 2024 18:37:44 +0300 Subject: [PATCH 05/10] codecov fix --- build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index cbed446..5d10260 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -159,8 +159,8 @@ tasks.jar { tasks.jacocoTestReport { reports { - xml.isEnabled = false - csv.isEnabled = true + xml.required.set(true) + html.required.set(false) } } From 721fd5d0c435104b3338fe652d05e4bb57549541 Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 17 Dec 2025 09:51:02 +0300 Subject: [PATCH 06/10] issue 191 --- build.gradle.kts | 28 ++++++++++--------- gradle/wrapper/gradle-wrapper.properties | 4 +-- .../xpathqs/core/reflection/SelectorParser.kt | 16 +++++++++-- .../SelectorReflectionFieldsNoScanTest.kt | 3 +- .../SelectorReflectionFieldsTest.kt | 6 +++- .../xpathqs/core/reflection/pages/pages.kt | 4 +-- .../ObjectWithBaseAndGroupObjectTest.kt | 4 +-- .../ObjectWithBaseAndInnerObjectTest.kt | 4 +-- .../parser/ObjectWithClassArgBlockTest.kt | 6 ++-- .../parser/ObjectWithClassBlockTest.kt | 4 +-- .../ObjectWithInnerObjectClassArgTest.kt | 3 +- .../reflection/parser/ObjectWithNoScanTest.kt | 4 +-- .../ObjectWithoutBaseForInhBlockTest.kt | 4 +-- .../parser/ObjectWithoutBaseTest.kt | 4 +-- .../selector/block/BlockSelectorCloneTest.kt | 4 +-- .../extensions/PageObjectCloneGroupTests.kt | 4 +-- .../extensions/PageObjectCloneTests.kt | 4 +-- .../selector/extensions/SelectorCloneTests.kt | 6 ++-- .../extensions/SelectorGetExtensionTests.kt | 4 +-- .../SelectorObjectModificationTests.kt | 4 +-- 20 files changed, 69 insertions(+), 51 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 5d10260..89de9c3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 XPATH-QS + * Copyright (c) 2025 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,7 +20,6 @@ * SOFTWARE. */ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.gradle.api.tasks.testing.logging.TestLogEvent version = "0.1.6" @@ -28,23 +27,23 @@ group = "org.xpathqs" val kotestVersion = "5.8.0" plugins { - kotlin("jvm") version "2.0.0-Beta4" + id("org.jetbrains.kotlin.jvm") version "2.3.0" id("org.jetbrains.dokka") version "1.4.32" `java-library` jacoco `maven-publish` signing id("io.codearte.nexus-staging") version "0.30.0" - id("io.gitlab.arturbosch.detekt").version("1.23.5") - id("info.solidsoft.pitest").version("1.9.0") - id("org.jetbrains.kotlinx.kover") version "0.6.1" + id("io.gitlab.arturbosch.detekt").version("1.23.8") + id("info.solidsoft.pitest").version("1.19.0-rc.2") + id("org.jetbrains.kotlinx.kover") version "0.9.4" } java { withJavadocJar() withSourcesJar() - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } jacoco { @@ -57,13 +56,15 @@ repositories { } detekt { - config = files("$projectDir/detekt.yml") // point to your custom config defining rules to run, overwriting default behavior + config.from( + files("$projectDir/detekt.yml") + ) } dependencies { implementation("io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.30.0") - implementation("org.jetbrains.kotlin:kotlin-reflect:2.0.0-Beta4") - implementation("org.yaml:snakeyaml:1.28") + implementation("org.jetbrains.kotlin:kotlin-reflect:2.3.0") + implementation("org.yaml:snakeyaml:2.0") implementation("net.oneandone.reflections8:reflections8:0.11.7") testImplementation("org.xpathqs:gwt:0.2.5") @@ -142,8 +143,9 @@ tasks.test { } } -tasks.withType { - kotlinOptions.jvmTarget = "11" + +kotlin { + jvmToolchain(17) } tasks.jar { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5b7ec27..c0b6a58 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2023 XPATH-QS +# Copyright (c) 2025 XPATH-QS # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -22,6 +22,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/kotlin/org/xpathqs/core/reflection/SelectorParser.kt b/src/main/kotlin/org/xpathqs/core/reflection/SelectorParser.kt index fe626b6..209414c 100644 --- a/src/main/kotlin/org/xpathqs/core/reflection/SelectorParser.kt +++ b/src/main/kotlin/org/xpathqs/core/reflection/SelectorParser.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 XPATH-QS + * Copyright (c) 2025 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -89,11 +89,11 @@ class SelectorParser( rootObj.children = srf.innerSelectors srf.innerSelectorProps.filter { - it.call(rootObj) !is NullSelector + safeEvaluate(it) !is NullSelector }.forEach { prop -> prop.isAccessible = true - val sel = prop.call(rootObj) as BaseSelector + val sel = safeEvaluate(prop) as BaseSelector val ann = prop.findAnnotation()?.value ?: prop.name setFields( to = sel, @@ -180,4 +180,14 @@ class SelectorParser( to.freeze() } } + + private fun safeEvaluate(prop: KProperty<*>) : Any? { + return runCatching { + prop.call(rootObj) + }.getOrNull() ?: run { + runCatching { + prop.call() + }.getOrNull() + } + } } \ No newline at end of file diff --git a/src/test/kotlin/org/xpathqs/core/reflection/SelectorReflectionFieldsNoScanTest.kt b/src/test/kotlin/org/xpathqs/core/reflection/SelectorReflectionFieldsNoScanTest.kt index 96f5e03..69b5f25 100644 --- a/src/test/kotlin/org/xpathqs/core/reflection/SelectorReflectionFieldsNoScanTest.kt +++ b/src/test/kotlin/org/xpathqs/core/reflection/SelectorReflectionFieldsNoScanTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2025 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,6 +24,7 @@ package org.xpathqs.core.reflection import io.kotest.core.spec.style.AnnotationSpec import io.kotest.matchers.collections.shouldHaveSize +import org.xpathqs.core.reflection.pages.PageWithNoScan class SelectorReflectionFieldsNoScanTest : AnnotationSpec() { diff --git a/src/test/kotlin/org/xpathqs/core/reflection/SelectorReflectionFieldsTest.kt b/src/test/kotlin/org/xpathqs/core/reflection/SelectorReflectionFieldsTest.kt index 5e49d63..c7120b4 100644 --- a/src/test/kotlin/org/xpathqs/core/reflection/SelectorReflectionFieldsTest.kt +++ b/src/test/kotlin/org/xpathqs/core/reflection/SelectorReflectionFieldsTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 XPATH-QS + * Copyright (c) 2025 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,6 +24,10 @@ package org.xpathqs.core.reflection import io.kotest.core.spec.style.AnnotationSpec import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder +import org.xpathqs.core.reflection.pages.PagePrivateMember +import org.xpathqs.core.reflection.pages.PageWithBase +import org.xpathqs.core.reflection.pages.PageWithBaseAndInnerGroupObject +import org.xpathqs.core.reflection.pages.PageWithBaseAndInnerObject import org.xpathqs.core.selector.base.BaseSelector import org.xpathqs.core.selector.selector.Selector diff --git a/src/test/kotlin/org/xpathqs/core/reflection/pages/pages.kt b/src/test/kotlin/org/xpathqs/core/reflection/pages/pages.kt index 1312839..6399391 100644 --- a/src/test/kotlin/org/xpathqs/core/reflection/pages/pages.kt +++ b/src/test/kotlin/org/xpathqs/core/reflection/pages/pages.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 XPATH-QS + * Copyright (c) 2025 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,7 +20,7 @@ * SOFTWARE. */ -package org.xpathqs.core.reflection +package org.xpathqs.core.reflection.pages import org.xpathqs.core.annotations.NoScan import org.xpathqs.core.selector.base.BaseSelector diff --git a/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithBaseAndGroupObjectTest.kt b/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithBaseAndGroupObjectTest.kt index 89db4ee..b4de213 100644 --- a/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithBaseAndGroupObjectTest.kt +++ b/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithBaseAndGroupObjectTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2025 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,7 +26,7 @@ package org.xpathqs.core.reflection.parser import io.kotest.core.spec.style.AnnotationSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.shouldBe -import org.xpathqs.core.reflection.PageWithBaseAndInnerGroupObject +import org.xpathqs.core.reflection.pages.PageWithBaseAndInnerGroupObject import org.xpathqs.core.reflection.SelectorParser import org.xpathqs.xpathShouldBe diff --git a/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithBaseAndInnerObjectTest.kt b/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithBaseAndInnerObjectTest.kt index 8363cef..802adb4 100644 --- a/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithBaseAndInnerObjectTest.kt +++ b/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithBaseAndInnerObjectTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2025 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,7 +26,7 @@ import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.AnnotationSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.shouldBe -import org.xpathqs.core.reflection.PageWithBaseAndInnerObject +import org.xpathqs.core.reflection.pages.PageWithBaseAndInnerObject import org.xpathqs.core.reflection.SelectorParser import org.xpathqs.core.selector.extensions.core.get import org.xpathqs.xpathShouldBe diff --git a/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithClassArgBlockTest.kt b/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithClassArgBlockTest.kt index 8741c5d..e177550 100644 --- a/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithClassArgBlockTest.kt +++ b/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithClassArgBlockTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2025 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,8 +24,8 @@ package org.xpathqs.core.reflection.parser import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.AnnotationSpec -import org.xpathqs.core.reflection.PageWithBlockArgMembers -import org.xpathqs.core.reflection.PageWithBlockMembers +import org.xpathqs.core.reflection.pages.PageWithBlockArgMembers +import org.xpathqs.core.reflection.pages.PageWithBlockMembers import org.xpathqs.core.reflection.SelectorParser import org.xpathqs.core.selector.extensions.core.get import org.xpathqs.nameShouldBe diff --git a/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithClassBlockTest.kt b/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithClassBlockTest.kt index aa633f7..00cc02b 100644 --- a/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithClassBlockTest.kt +++ b/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithClassBlockTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2025 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,7 +25,7 @@ package org.xpathqs.core.reflection.parser import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.AnnotationSpec import io.kotest.matchers.shouldNotBe -import org.xpathqs.core.reflection.PageWithBlockMembers +import org.xpathqs.core.reflection.pages.PageWithBlockMembers import org.xpathqs.core.reflection.SelectorParser import org.xpathqs.core.selector.extensions.core.get import org.xpathqs.nameShouldBe diff --git a/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithInnerObjectClassArgTest.kt b/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithInnerObjectClassArgTest.kt index 48449d7..e845408 100644 --- a/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithInnerObjectClassArgTest.kt +++ b/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithInnerObjectClassArgTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2025 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,6 +26,7 @@ import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.AnnotationSpec import io.kotest.matchers.collections.shouldHaveSize import org.xpathqs.core.reflection.* +import org.xpathqs.core.reflection.pages.PageWithInnerObjectClassArg import org.xpathqs.core.selector.extensions.core.get import org.xpathqs.nameShouldBe import org.xpathqs.xpathShouldBe diff --git a/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithNoScanTest.kt b/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithNoScanTest.kt index 07f4ff6..e6a58bc 100644 --- a/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithNoScanTest.kt +++ b/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithNoScanTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2025 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,7 +25,7 @@ package org.xpathqs.core.reflection.parser import io.kotest.core.spec.style.AnnotationSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldBeEmpty -import org.xpathqs.core.reflection.PageWithNoScan +import org.xpathqs.core.reflection.pages.PageWithNoScan import org.xpathqs.core.reflection.SelectorParser import org.xpathqs.xpathShouldBe diff --git a/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithoutBaseForInhBlockTest.kt b/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithoutBaseForInhBlockTest.kt index f4b73cd..3cebcca 100644 --- a/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithoutBaseForInhBlockTest.kt +++ b/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithoutBaseForInhBlockTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2025 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,7 @@ package org.xpathqs.core.reflection.parser import io.kotest.core.spec.style.AnnotationSpec -import org.xpathqs.core.reflection.PageWithInhGroup +import org.xpathqs.core.reflection.pages.PageWithInhGroup import org.xpathqs.core.reflection.SelectorParser import org.xpathqs.xpathShouldBe diff --git a/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithoutBaseTest.kt b/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithoutBaseTest.kt index 27a1d9e..5d53b63 100644 --- a/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithoutBaseTest.kt +++ b/src/test/kotlin/org/xpathqs/core/reflection/parser/ObjectWithoutBaseTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2025 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,7 @@ package org.xpathqs.core.reflection.parser import io.kotest.core.spec.style.AnnotationSpec -import org.xpathqs.core.reflection.PageNoBase +import org.xpathqs.core.reflection.pages.PageNoBase import org.xpathqs.core.reflection.SelectorParser import org.xpathqs.xpathShouldBe diff --git a/src/test/kotlin/org/xpathqs/core/selector/block/BlockSelectorCloneTest.kt b/src/test/kotlin/org/xpathqs/core/selector/block/BlockSelectorCloneTest.kt index 4306bac..6a9c45e 100644 --- a/src/test/kotlin/org/xpathqs/core/selector/block/BlockSelectorCloneTest.kt +++ b/src/test/kotlin/org/xpathqs/core/selector/block/BlockSelectorCloneTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2025 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,7 +24,7 @@ package org.xpathqs.core.selector.block import io.kotest.core.spec.style.AnnotationSpec import io.kotest.matchers.types.shouldNotBeSameInstanceAs -import org.xpathqs.core.reflection.SomeHolder +import org.xpathqs.core.reflection.pages.SomeHolder import org.xpathqs.core.reflection.parse import org.xpathqs.core.selector.base.BaseSelector import org.xpathqs.core.selector.extensions.core.get diff --git a/src/test/kotlin/org/xpathqs/core/selector/extensions/PageObjectCloneGroupTests.kt b/src/test/kotlin/org/xpathqs/core/selector/extensions/PageObjectCloneGroupTests.kt index fd49751..e8714ed 100644 --- a/src/test/kotlin/org/xpathqs/core/selector/extensions/PageObjectCloneGroupTests.kt +++ b/src/test/kotlin/org/xpathqs/core/selector/extensions/PageObjectCloneGroupTests.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2025 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,7 +28,7 @@ import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import io.kotest.matchers.types.shouldBeSameInstanceAs import io.kotest.matchers.types.shouldNotBeSameInstanceAs -import org.xpathqs.core.reflection.PageWithGroupBase +import org.xpathqs.core.reflection.pages.PageWithGroupBase import org.xpathqs.core.reflection.SelectorParser import org.xpathqs.core.selector.block.deepClone diff --git a/src/test/kotlin/org/xpathqs/core/selector/extensions/PageObjectCloneTests.kt b/src/test/kotlin/org/xpathqs/core/selector/extensions/PageObjectCloneTests.kt index eeca57b..70b6782 100644 --- a/src/test/kotlin/org/xpathqs/core/selector/extensions/PageObjectCloneTests.kt +++ b/src/test/kotlin/org/xpathqs/core/selector/extensions/PageObjectCloneTests.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2025 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,7 +27,7 @@ import io.kotest.core.spec.style.AnnotationSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.types.shouldBeSameInstanceAs import io.kotest.matchers.types.shouldNotBeSameInstanceAs -import org.xpathqs.core.reflection.PageWithBase +import org.xpathqs.core.reflection.pages.PageWithBase import org.xpathqs.core.reflection.SelectorParser import org.xpathqs.core.selector.block.deepClone diff --git a/src/test/kotlin/org/xpathqs/core/selector/extensions/SelectorCloneTests.kt b/src/test/kotlin/org/xpathqs/core/selector/extensions/SelectorCloneTests.kt index 73428c9..eefc442 100644 --- a/src/test/kotlin/org/xpathqs/core/selector/extensions/SelectorCloneTests.kt +++ b/src/test/kotlin/org/xpathqs/core/selector/extensions/SelectorCloneTests.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2025 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,8 +24,8 @@ package org.xpathqs.core.selector.extensions import io.kotest.core.spec.style.AnnotationSpec import io.kotest.matchers.types.shouldNotBeSameInstanceAs -import org.xpathqs.core.reflection.PageWithBaseWithChain -import org.xpathqs.core.reflection.PageWithBaseWithChainXpath +import org.xpathqs.core.reflection.pages.PageWithBaseWithChain +import org.xpathqs.core.reflection.pages.PageWithBaseWithChainXpath import org.xpathqs.core.reflection.SelectorParser import org.xpathqs.core.reflection.freeze import org.xpathqs.core.selector.selector.Selector diff --git a/src/test/kotlin/org/xpathqs/core/selector/extensions/SelectorGetExtensionTests.kt b/src/test/kotlin/org/xpathqs/core/selector/extensions/SelectorGetExtensionTests.kt index 2306d20..63feda0 100644 --- a/src/test/kotlin/org/xpathqs/core/selector/extensions/SelectorGetExtensionTests.kt +++ b/src/test/kotlin/org/xpathqs/core/selector/extensions/SelectorGetExtensionTests.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2025 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,7 +24,7 @@ package org.xpathqs.core.selector.extensions import io.kotest.core.spec.style.AnnotationSpec import io.kotest.matchers.shouldBe -import org.xpathqs.core.reflection.PageWithBase +import org.xpathqs.core.reflection.pages.PageWithBase import org.xpathqs.core.reflection.SelectorParser class SelectorGetExtensionTests : AnnotationSpec() { diff --git a/src/test/kotlin/org/xpathqs/core/selector/extensions/SelectorObjectModificationTests.kt b/src/test/kotlin/org/xpathqs/core/selector/extensions/SelectorObjectModificationTests.kt index c600b11..5a0a152 100644 --- a/src/test/kotlin/org/xpathqs/core/selector/extensions/SelectorObjectModificationTests.kt +++ b/src/test/kotlin/org/xpathqs/core/selector/extensions/SelectorObjectModificationTests.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2025 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,7 +24,7 @@ package org.xpathqs.core.selector.extensions import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.AnnotationSpec -import org.xpathqs.core.reflection.PageWithBase +import org.xpathqs.core.reflection.pages.PageWithBase import org.xpathqs.core.reflection.SelectorParser import org.xpathqs.core.reflection.parse import org.xpathqs.core.selector.block.Block From e25651f94d0479bf638d734d6a9f93682e2f7018 Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 17 Dec 2025 23:07:15 +0300 Subject: [PATCH 07/10] issue 191 --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 58bf0da..2738bdc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,7 +28,7 @@ jobs: java-version: 15 - name: Cache Gradle packages - uses: actions/cache@v1 + uses: actions/cache@v4 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} @@ -41,7 +41,7 @@ jobs: run: ./gradlew clean build - name: Upload test report directory - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 if: always() with: name: testDebugStuff From 45e8cacfbfd16b60d25764ed6e4bb7a842d0abc7 Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 17 Dec 2025 23:08:44 +0300 Subject: [PATCH 08/10] issue 191 --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2738bdc..1c9c08d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,10 +22,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 15 + - name: Set up JDK 17 uses: actions/setup-java@v1 with: - java-version: 15 + java-version: 17 - name: Cache Gradle packages uses: actions/cache@v4 From dc4eb8e60e06629b7613bff3d6a84076be8bbde9 Mon Sep 17 00:00:00 2001 From: Nikita Date: Sat, 31 Oct 2026 05:36:47 +0300 Subject: [PATCH 09/10] issue 193 --- build.gradle.kts | 3 +- .../extensions/SelectorArgExtensions.kt | 16 ++++----- .../GroupSelectorModificationExtensions.kt | 20 +++++------ .../xpathqs/core/reflection/SingleBaseTest.kt | 22 ++++++------ .../extensions/ReflectionExtensionsTest.kt | 12 +++---- .../reflection/extensions/RootParentTest.kt | 14 ++++---- .../testpackage/PackageScannerExtensions.kt | 14 ++++---- .../core/selector/args/SelectorArgTest.kt | 13 +++---- .../core/selector/args/SelectorArgsTests.kt | 6 ++-- .../KVNormalizeSpaceDecoratorTest.kt | 9 ++--- .../base/BaseSelectorCustomPropsMapTest.kt | 5 +-- .../selector/base/BaseSelectorToStringTest.kt | 7 ++-- .../base/extension/CanBeClonedTest.kt | 7 ++-- .../selector/base/extension/DeepCloneTest.kt | 4 +-- .../selector/base/extension/ParentsTest.kt | 35 ++++++++++--------- .../selector/block/BlockSelectorXpathTest.kt | 19 +++++----- .../core/selector/block/CopyFromTest.kt | 9 ++--- .../block/extensions/InnerSelectorsTest.kt | 28 +++++++-------- .../group/extensions/AddGroupArgTest.kt | 7 ++-- .../selector/group/extensions/PrefixTest.kt | 11 +++--- 20 files changed, 133 insertions(+), 128 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 89de9c3..806fafe 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -51,7 +51,6 @@ jacoco { } repositories { - mavenLocal() mavenCentral() } diff --git a/src/main/kotlin/org/xpathqs/core/selector/extensions/SelectorArgExtensions.kt b/src/main/kotlin/org/xpathqs/core/selector/extensions/SelectorArgExtensions.kt index 365e94a..9b39e16 100644 --- a/src/main/kotlin/org/xpathqs/core/selector/extensions/SelectorArgExtensions.kt +++ b/src/main/kotlin/org/xpathqs/core/selector/extensions/SelectorArgExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,6 +32,8 @@ import org.xpathqs.core.selector.args.decorators.ContainsDecorator import org.xpathqs.core.selector.args.decorators.KVNormalizeSpaceDecorator import org.xpathqs.core.selector.base.BaseSelector import org.xpathqs.core.selector.extensions.core.clone +import org.xpathqs.core.selector.group.GroupSelector +import org.xpathqs.core.selector.group.addGroupArg /** * Add `text` argument query @@ -69,10 +71,7 @@ fun T.arg( argName: String, value: String, contains: Boolean = false, - normalize: Boolean = false -) = selfClone { - val res = this.clone() - + normalize: Boolean = false): T { var arg: ValueArg = CommaDecorator(KVSelectorArg(argName, value)) @@ -84,9 +83,10 @@ fun T.arg( arg = ContainsDecorator(arg as KVSelectorArg) } - res.props.args.add( - arg - ) + return when (this) { + is GroupSelector -> this.addGroupArg(arg) + else -> this.addArg(arg) + } } /** diff --git a/src/main/kotlin/org/xpathqs/core/selector/group/GroupSelectorModificationExtensions.kt b/src/main/kotlin/org/xpathqs/core/selector/group/GroupSelectorModificationExtensions.kt index c052024..8409058 100644 --- a/src/main/kotlin/org/xpathqs/core/selector/group/GroupSelectorModificationExtensions.kt +++ b/src/main/kotlin/org/xpathqs/core/selector/group/GroupSelectorModificationExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -72,23 +72,19 @@ fun T.prefix(value: String): T { /** - * Modifies `prefix` value of [GroupSelector] + * Modifies `postfix` value of [GroupSelector] * - * Require #1 - Prefix should be updated only when [GroupSelector.selectorsChain] - * has first [Selector] element. It is actual for the Block elements + * Require #1 - postfix should be updated only when [GroupSelector.selectorsChain] + * has last [Selector] element. It is actual for the Block elements * @sample [org.xpathqs.core.selector.group.extensions.PrefixTest.r1_prefixWithSelector] * - * Require #2 - prefix should be ignored when first selector is not a [Selector] object - * @sample [org.xpathqs.core.selector.group.extensions.PrefixTest.r2_prefixWithXpathSelector] */ fun T.postfix(value: String): T { - val first = this.selectorsChain.first() - if (first is Selector) { + val last = this.selectorsChain.last() + if (last is Selector) { val res = this.clone() - val chain: ArrayList = arrayListOf(first.postfix(value)) - this.selectorsChain.removeFirst() - chain.addAll(this.selectorsChain) - res.selectorsChain = chain + this.selectorsChain.removeLast() + res.selectorsChain = ArrayList( this.selectorsChain + last.postfix(value)) return res } diff --git a/src/test/kotlin/org/xpathqs/core/reflection/SingleBaseTest.kt b/src/test/kotlin/org/xpathqs/core/reflection/SingleBaseTest.kt index 3cb2771..fc1e9fa 100644 --- a/src/test/kotlin/org/xpathqs/core/reflection/SingleBaseTest.kt +++ b/src/test/kotlin/org/xpathqs/core/reflection/SingleBaseTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -56,9 +56,9 @@ class SingleBaseTest : AnnotationSpec() { fun r1_forSelector() { WHEN { ForTest().parse().s1.toXpath() - }.THEN { + }.THEN( "//base/div" - } + ) } /** @@ -71,9 +71,9 @@ class SingleBaseTest : AnnotationSpec() { ForTestObj.parse() }.WHEN { ForTestObj.InnerObj.s1.toXpath() - }.THEN { + }.THEN( "//base/div" - } + ) } /** @@ -84,9 +84,9 @@ class SingleBaseTest : AnnotationSpec() { fun r3_forGroupSelector() { WHEN { ForTest().parse().s2.toXpath() - }.THEN { + }.THEN( "//base/div//div" - } + ) } /** @@ -99,9 +99,9 @@ class SingleBaseTest : AnnotationSpec() { ForTestObj.parse() }.WHEN { ForTestObj.InnerObj.s2.toXpath() - }.THEN { + }.THEN( "//base/div" - } + ) } /** @@ -114,8 +114,8 @@ class SingleBaseTest : AnnotationSpec() { ForTestObj.parse() }.WHEN { ForTestObj.InnerObj.s3.toXpath() - }.THEN { + }.THEN( "//base//div" - } + ) } } diff --git a/src/test/kotlin/org/xpathqs/core/reflection/extensions/ReflectionExtensionsTest.kt b/src/test/kotlin/org/xpathqs/core/reflection/extensions/ReflectionExtensionsTest.kt index 8493efe..cfea933 100644 --- a/src/test/kotlin/org/xpathqs/core/reflection/extensions/ReflectionExtensionsTest.kt +++ b/src/test/kotlin/org/xpathqs/core/reflection/extensions/ReflectionExtensionsTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,7 @@ import org.xpathqs.gwt.GIVEN class ReflectionExtensionsTest : AnnotationSpec() { class TestCls { - inner class InnerCls + class InnerCls val innerMember = InnerCls() class Class2 val member = Class2() @@ -44,9 +44,9 @@ class ReflectionExtensionsTest : AnnotationSpec() { tagSelector("div") }.WHEN { given.isInnerClass() - }.THEN { + }.THEN( false - } + ) } /** @@ -58,8 +58,8 @@ class ReflectionExtensionsTest : AnnotationSpec() { TestCls() }.WHEN { given.innerMember.isInnerClass() - }.THEN { + }.THEN( true - } + ) } } \ No newline at end of file diff --git a/src/test/kotlin/org/xpathqs/core/reflection/extensions/RootParentTest.kt b/src/test/kotlin/org/xpathqs/core/reflection/extensions/RootParentTest.kt index f68be33..88b4c12 100644 --- a/src/test/kotlin/org/xpathqs/core/reflection/extensions/RootParentTest.kt +++ b/src/test/kotlin/org/xpathqs/core/reflection/extensions/RootParentTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -39,9 +39,9 @@ class RootParentTest : AnnotationSpec() { fun r1_rootParent() { WHEN { tagSelector("div").rootParent - }.THEN { + }.THEN( NullSelector() - } + ) } /** @@ -51,9 +51,9 @@ class RootParentTest : AnnotationSpec() { fun r2_rootParent() { WHEN { RootParent.level1.rootParent - }.THEN { + }.THEN( RootParent - } + ) } /** @@ -63,9 +63,9 @@ class RootParentTest : AnnotationSpec() { fun r3_rootParent() { WHEN { RootParent.InnerObj.level2.rootParent - }.THEN { + }.THEN( RootParent - } + ) } init { RootParent.parse() diff --git a/src/test/kotlin/org/xpathqs/core/reflection/testpackage/PackageScannerExtensions.kt b/src/test/kotlin/org/xpathqs/core/reflection/testpackage/PackageScannerExtensions.kt index f086d7b..cb6c078 100644 --- a/src/test/kotlin/org/xpathqs/core/reflection/testpackage/PackageScannerExtensions.kt +++ b/src/test/kotlin/org/xpathqs/core/reflection/testpackage/PackageScannerExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -46,9 +46,9 @@ class PackageScannerExtensions : AnnotationSpec() { this.javaClass.`package`.scan() }.WHEN { Page1.s1.name - }.THEN { + }.THEN( "Page1.s1" - } + ) } /** @@ -60,9 +60,9 @@ class PackageScannerExtensions : AnnotationSpec() { "org.xpathqs.core.reflection.testpage2".scanPackage() }.WHEN { Page3.s1.name - }.THEN { + }.THEN( "Page3.s1" - } + ) } /** @@ -74,8 +74,8 @@ class PackageScannerExtensions : AnnotationSpec() { scanPackage(Page2) }.WHEN { Page2.s1.name - }.THEN { + }.THEN( "Page2.s1" - } + ) } } \ No newline at end of file diff --git a/src/test/kotlin/org/xpathqs/core/selector/args/SelectorArgTest.kt b/src/test/kotlin/org/xpathqs/core/selector/args/SelectorArgTest.kt index 8462c7d..3d3dea9 100644 --- a/src/test/kotlin/org/xpathqs/core/selector/args/SelectorArgTest.kt +++ b/src/test/kotlin/org/xpathqs/core/selector/args/SelectorArgTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,7 @@ package org.xpathqs.core.selector.args import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.matchers.shouldBe import org.xpathqs.core.selector.selector.preceding import org.xpathqs.core.selector.selector.prefix import org.xpathqs.core.util.SelectorFactory.tagSelector @@ -42,7 +43,7 @@ class SelectorArgTest : AnnotationSpec() { SelectorArg(given) .value }.THEN { - given.toXpath() + given.toXpath() shouldBe actual } } @@ -57,7 +58,7 @@ class SelectorArgTest : AnnotationSpec() { SelectorArg(given, type = InnerSelectorArg.ALL) .value }.THEN { - given.prefix(".//").toXpath() + given.prefix(".//").toXpath() shouldBe actual } } @@ -72,7 +73,7 @@ class SelectorArgTest : AnnotationSpec() { SelectorArg(given, type = InnerSelectorArg.ROOT) .value }.THEN { - given.prefix("./").toXpath() + given.prefix("./").toXpath() shouldBe actual } } @@ -87,7 +88,7 @@ class SelectorArgTest : AnnotationSpec() { SelectorArg(given, type = InnerSelectorArg.ROOT) .value }.THEN { - given.toXpath() + given.toXpath() shouldBe actual } } @@ -102,7 +103,7 @@ class SelectorArgTest : AnnotationSpec() { SelectorArg(given, type = InnerSelectorArg.ROOT) .value }.THEN { - "./preceding::div" + "./preceding::div" shouldBe actual } } } \ No newline at end of file diff --git a/src/test/kotlin/org/xpathqs/core/selector/args/SelectorArgsTests.kt b/src/test/kotlin/org/xpathqs/core/selector/args/SelectorArgsTests.kt index 13233a4..39ac17f 100644 --- a/src/test/kotlin/org/xpathqs/core/selector/args/SelectorArgsTests.kt +++ b/src/test/kotlin/org/xpathqs/core/selector/args/SelectorArgsTests.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -89,8 +89,8 @@ class SelectorArgsTests : AnnotationSpec() { SelectorArgs() .add(tagSelector("div")) .toXpath() - }.THEN { + }.THEN( "[//div]" - } + ) } } diff --git a/src/test/kotlin/org/xpathqs/core/selector/args/decorators/KVNormalizeSpaceDecoratorTest.kt b/src/test/kotlin/org/xpathqs/core/selector/args/decorators/KVNormalizeSpaceDecoratorTest.kt index 588d325..e8c0df0 100644 --- a/src/test/kotlin/org/xpathqs/core/selector/args/decorators/KVNormalizeSpaceDecoratorTest.kt +++ b/src/test/kotlin/org/xpathqs/core/selector/args/decorators/KVNormalizeSpaceDecoratorTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,7 @@ package org.xpathqs.core.selector.args.decorators import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.matchers.shouldBe import org.xpathqs.core.selector.args.KVSelectorArg import org.xpathqs.gwt.WHEN @@ -38,7 +39,7 @@ class KVNormalizeSpaceDecoratorTest : AnnotationSpec() { KVNormalizeSpaceDecorator(arg) .toXpath() }.THEN { - "normalize-space(text())=text" + "normalize-space(text())=text" shouldBe actual } } @@ -51,7 +52,7 @@ class KVNormalizeSpaceDecoratorTest : AnnotationSpec() { KVNormalizeSpaceDecorator(arg, normalizeK = true, normalizeV = true) .toXpath() }.THEN { - "normalize-space(text())=normalize-space(text)" + "normalize-space(text())=normalize-space(text)" shouldBe actual } } @@ -64,7 +65,7 @@ class KVNormalizeSpaceDecoratorTest : AnnotationSpec() { KVNormalizeSpaceDecorator(arg, normalizeK = false, normalizeV = false) .toXpath() }.THEN { - "text()=text" + "text()=text" shouldBe actual } } } \ No newline at end of file diff --git a/src/test/kotlin/org/xpathqs/core/selector/base/BaseSelectorCustomPropsMapTest.kt b/src/test/kotlin/org/xpathqs/core/selector/base/BaseSelectorCustomPropsMapTest.kt index 170e161..8f0f5da 100644 --- a/src/test/kotlin/org/xpathqs/core/selector/base/BaseSelectorCustomPropsMapTest.kt +++ b/src/test/kotlin/org/xpathqs/core/selector/base/BaseSelectorCustomPropsMapTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,7 @@ package org.xpathqs.core.selector.base import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.matchers.shouldBe import org.xpathqs.core.selector.extensions.core.clone import org.xpathqs.core.util.SelectorFactory.tagSelector import org.xpathqs.gwt.GIVEN @@ -42,7 +43,7 @@ class BaseSelectorCustomPropsMapTest : AnnotationSpec() { }.WHEN { given.clone() }.THEN { - given.customPropsMap === actual.customPropsMap + (given.customPropsMap === actual.customPropsMap) shouldBe true } } diff --git a/src/test/kotlin/org/xpathqs/core/selector/base/BaseSelectorToStringTest.kt b/src/test/kotlin/org/xpathqs/core/selector/base/BaseSelectorToStringTest.kt index 8a95c19..c4d1d3b 100644 --- a/src/test/kotlin/org/xpathqs/core/selector/base/BaseSelectorToStringTest.kt +++ b/src/test/kotlin/org/xpathqs/core/selector/base/BaseSelectorToStringTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,7 @@ package org.xpathqs.core.selector.base import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.matchers.shouldBe import org.xpathqs.core.reflection.parse import org.xpathqs.core.selector.block.Block import org.xpathqs.core.util.SelectorFactory.tagSelector @@ -42,7 +43,7 @@ class BaseSelectorToStringTest : AnnotationSpec() { WHEN { TestCls().parse().s1.toString() }.THEN { - "TestCls.s1" + "TestCls.s1" shouldBe actual } /** @@ -54,6 +55,6 @@ class BaseSelectorToStringTest : AnnotationSpec() { WHEN { tagSelector("div").toString() }.THEN { - "//div" + "//div" shouldBe actual } } \ No newline at end of file diff --git a/src/test/kotlin/org/xpathqs/core/selector/base/extension/CanBeClonedTest.kt b/src/test/kotlin/org/xpathqs/core/selector/base/extension/CanBeClonedTest.kt index 3bc2d50..2944600 100644 --- a/src/test/kotlin/org/xpathqs/core/selector/base/extension/CanBeClonedTest.kt +++ b/src/test/kotlin/org/xpathqs/core/selector/base/extension/CanBeClonedTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,7 @@ package org.xpathqs.core.selector.base.extension import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.matchers.shouldBe import org.xpathqs.core.reflection.parse import org.xpathqs.core.selector.base.canBeCloned import org.xpathqs.core.selector.block.Block @@ -51,7 +52,7 @@ class CanBeClonedTest : AnnotationSpec() { }.WHEN { R1.m.canBeCloned() }.THEN { - false + false shouldBe actual } } @@ -66,7 +67,7 @@ class CanBeClonedTest : AnnotationSpec() { }.WHEN { given.m.canBeCloned() }.THEN { - true + true shouldBe actual } } } \ No newline at end of file diff --git a/src/test/kotlin/org/xpathqs/core/selector/base/extension/DeepCloneTest.kt b/src/test/kotlin/org/xpathqs/core/selector/base/extension/DeepCloneTest.kt index 778bf49..cfd9ce9 100644 --- a/src/test/kotlin/org/xpathqs/core/selector/base/extension/DeepCloneTest.kt +++ b/src/test/kotlin/org/xpathqs/core/selector/base/extension/DeepCloneTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -87,7 +87,7 @@ class DeepCloneTest : AnnotationSpec() { }.WHEN { given.deepClone().state }.THEN { - SelectorState.CLONED + SelectorState.CLONED shouldBe actual } } } \ No newline at end of file diff --git a/src/test/kotlin/org/xpathqs/core/selector/base/extension/ParentsTest.kt b/src/test/kotlin/org/xpathqs/core/selector/base/extension/ParentsTest.kt index 99c377a..c6855aa 100644 --- a/src/test/kotlin/org/xpathqs/core/selector/base/extension/ParentsTest.kt +++ b/src/test/kotlin/org/xpathqs/core/selector/base/extension/ParentsTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,7 @@ package org.xpathqs.core.selector.base.extension import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.matchers.shouldBe import org.xpathqs.core.reflection.scanPackage import org.xpathqs.core.selector.block.Block import org.xpathqs.core.selector.block.allInnerSelectorBlocks @@ -64,7 +65,7 @@ class ParentsTest : AnnotationSpec() { WHEN { tagSelector("div").parents }.THEN { - emptyList() + emptyList() shouldBe actual } } @@ -76,7 +77,7 @@ class ParentsTest : AnnotationSpec() { WHEN { Page1.s1.parents }.THEN { - arrayListOf(Page1) + arrayListOf(Page1) shouldBe actual } } @@ -88,7 +89,7 @@ class ParentsTest : AnnotationSpec() { WHEN { Page1.Inner.s1.parents }.THEN { - arrayListOf(Page1.Inner, Page1) + arrayListOf(Page1.Inner, Page1) shouldBe actual } } @@ -100,7 +101,7 @@ class ParentsTest : AnnotationSpec() { WHEN { Page1.Inner isChildOf Page1 }.THEN { - true + true shouldBe actual } } @@ -112,7 +113,7 @@ class ParentsTest : AnnotationSpec() { WHEN { Page1 isChildOf Page1.Inner }.THEN { - false + false shouldBe actual } } @@ -124,7 +125,7 @@ class ParentsTest : AnnotationSpec() { WHEN { Page1 isChildOf Page1 }.THEN { - false + false shouldBe actual } } @@ -136,7 +137,7 @@ class ParentsTest : AnnotationSpec() { WHEN { Page1.Inner2.Inner3 isChildOf Page1.allInnerSelectorBlocks }.THEN { - true + true shouldBe actual } } @@ -148,7 +149,7 @@ class ParentsTest : AnnotationSpec() { WHEN { Page1.Inner isChildOf Page1.Inner2.allInnerSelectorBlocks }.THEN { - false + false shouldBe actual } } @@ -172,7 +173,7 @@ class ParentsTest : AnnotationSpec() { WHEN { Page1.Inner2 isParentOf Page1.Inner }.THEN { - false + false shouldBe actual } } @@ -184,7 +185,7 @@ class ParentsTest : AnnotationSpec() { WHEN { Page1.Inner isParentOf Page1.Inner }.THEN { - false + false shouldBe actual } } @@ -196,7 +197,7 @@ class ParentsTest : AnnotationSpec() { WHEN { Page1 isParentOf Page1.allInnerSelectorBlocks }.THEN { - true + true shouldBe actual } } @@ -208,7 +209,7 @@ class ParentsTest : AnnotationSpec() { WHEN { Page1.Inner isParentOf Page1.Inner2.allInnerSelectorBlocks }.THEN { - false + false shouldBe actual } } @@ -220,7 +221,7 @@ class ParentsTest : AnnotationSpec() { WHEN { Page1.Inner doesNotChildOf Page1 }.THEN { - false + false shouldBe actual } } @@ -232,7 +233,7 @@ class ParentsTest : AnnotationSpec() { WHEN { Page1.Inner doesNotChildOf Page1.allInnerSelectorBlocks }.THEN { - true + true shouldBe actual } } @@ -244,7 +245,7 @@ class ParentsTest : AnnotationSpec() { WHEN { Page1 doesNotParentOf Page1.Inner }.THEN { - false + false shouldBe actual } } @@ -256,7 +257,7 @@ class ParentsTest : AnnotationSpec() { WHEN { Page1 doesNotParentOf Page1.allInnerSelectorBlocks }.THEN { - false + false shouldBe actual } } } \ No newline at end of file diff --git a/src/test/kotlin/org/xpathqs/core/selector/block/BlockSelectorXpathTest.kt b/src/test/kotlin/org/xpathqs/core/selector/block/BlockSelectorXpathTest.kt index 95c3a55..1acc059 100644 --- a/src/test/kotlin/org/xpathqs/core/selector/block/BlockSelectorXpathTest.kt +++ b/src/test/kotlin/org/xpathqs/core/selector/block/BlockSelectorXpathTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,9 +26,10 @@ import io.kotest.core.spec.style.AnnotationSpec import org.xpathqs.core.reflection.parse import org.xpathqs.core.selector.extensions.core.clone import org.xpathqs.core.selector.extensions.core.get +import org.xpathqs.core.selector.extensions.text import org.xpathqs.core.util.SelectorFactory.tagSelector -import org.xpathqs.gwt.AND import org.xpathqs.gwt.GIVEN +import org.xpathqs.xpathShouldBe class BlockSelectorXpathTest : AnnotationSpec() { object TestCls : Block( @@ -39,6 +40,7 @@ class BlockSelectorXpathTest : AnnotationSpec() { init { TestCls.parse() + } /** @@ -50,13 +52,14 @@ class BlockSelectorXpathTest : AnnotationSpec() { GIVEN { TestCls[2].s }.WHEN { - given.xpath - given.xpath - }.THEN { - "//div[position()=2]//p" - }.THEN { given.toXpath() - } + }.THEN("//div[position()=2]//p") + } + + @Test + fun checkTextArgForBlock() { + TestCls.text("ss").s + .xpathShouldBe("//div[text()='ss']//p") } /** diff --git a/src/test/kotlin/org/xpathqs/core/selector/block/CopyFromTest.kt b/src/test/kotlin/org/xpathqs/core/selector/block/CopyFromTest.kt index 6c7c7df..c881c17 100644 --- a/src/test/kotlin/org/xpathqs/core/selector/block/CopyFromTest.kt +++ b/src/test/kotlin/org/xpathqs/core/selector/block/CopyFromTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,7 @@ package org.xpathqs.core.selector.block import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.matchers.shouldBe import org.xpathqs.core.reflection.parse import org.xpathqs.core.selector.extensions.plus import org.xpathqs.core.selector.group.GroupSelector @@ -59,7 +60,7 @@ class CopyFromTest : AnnotationSpec() { }.WHEN { given.s.toXpath() }.THEN { - "//div1//div" + "//div1//div" shouldBe actual } } @@ -75,7 +76,7 @@ class CopyFromTest : AnnotationSpec() { }.WHEN { given.s.toXpath() }.THEN { - "//div1//div" + "//div1//div" shouldBe actual } } @@ -91,7 +92,7 @@ class CopyFromTest : AnnotationSpec() { }.WHEN { given.s.toXpath() }.THEN { - "//div1//div2//div" + "//div1//div2//div" shouldBe actual } } } \ No newline at end of file diff --git a/src/test/kotlin/org/xpathqs/core/selector/block/extensions/InnerSelectorsTest.kt b/src/test/kotlin/org/xpathqs/core/selector/block/extensions/InnerSelectorsTest.kt index 5c45b93..321f0cd 100644 --- a/src/test/kotlin/org/xpathqs/core/selector/block/extensions/InnerSelectorsTest.kt +++ b/src/test/kotlin/org/xpathqs/core/selector/block/extensions/InnerSelectorsTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,8 @@ package org.xpathqs.core.selector.block.extensions import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.matchers.collections.shouldContainAll +import io.kotest.matchers.shouldBe import org.xpathqs.core.reflection.parse import org.xpathqs.core.selector.block.Block import org.xpathqs.core.selector.block.allInnerSelectors @@ -107,7 +109,7 @@ class InnerSelectorsTest : AnnotationSpec() { arrayListOf( PageWithoutInner.s1, PageWithoutInner.s2 - ) + ) shouldBe actual } } @@ -122,7 +124,7 @@ class InnerSelectorsTest : AnnotationSpec() { arrayListOf( PageWithInner.s1, PageWithInner.s2 - ) + ) shouldBe actual } } @@ -131,11 +133,7 @@ class InnerSelectorsTest : AnnotationSpec() { */ @Test fun r1_selectorBlocks() { - WHEN { - PageWithoutInner.selectorBlocks - }.THEN( - arrayListOf() - ) + PageWithoutInner.selectorBlocks shouldBe arrayListOf() } /** @@ -148,7 +146,7 @@ class InnerSelectorsTest : AnnotationSpec() { }.THEN { arrayListOf( PageWithInner.Inner - ) + ) shouldBe actual } } @@ -162,7 +160,7 @@ class InnerSelectorsTest : AnnotationSpec() { }.THEN { arrayListOf( PageWithInnerClass.Inner - ) + ) shouldBe actual } } @@ -177,7 +175,7 @@ class InnerSelectorsTest : AnnotationSpec() { arrayListOf( PageWithoutInner.s1, PageWithoutInner.s2, - ) + ) shouldContainAll actual } } @@ -194,7 +192,7 @@ class InnerSelectorsTest : AnnotationSpec() { PageWithInner.s2, PageWithInner.Inner.s1, PageWithInner.Inner.s2 - ) + ) shouldContainAll actual } } @@ -212,7 +210,7 @@ class InnerSelectorsTest : AnnotationSpec() { PageWithInnerClass.Inner, PageWithInnerClass.Inner.s1, PageWithInnerClass.Inner.s2 - ) + ) shouldContainAll actual } } @@ -230,7 +228,7 @@ class InnerSelectorsTest : AnnotationSpec() { PageWithInner2.Inner.Inner2.Inner3, PageWithInner2.Inner.Inner2.Inner3.Inner4, PageWithInner2.Inner.Inner2.Inner3.Inner4.Inner5, - ) + ) shouldContainAll actual } } @@ -242,7 +240,7 @@ class InnerSelectorsTest : AnnotationSpec() { WHEN { PageWithoutInner.allInnerSelectorBlocks }.THEN { - emptyList() + emptyList() shouldBe actual } } } \ No newline at end of file diff --git a/src/test/kotlin/org/xpathqs/core/selector/group/extensions/AddGroupArgTest.kt b/src/test/kotlin/org/xpathqs/core/selector/group/extensions/AddGroupArgTest.kt index 66fb315..4e0c826 100644 --- a/src/test/kotlin/org/xpathqs/core/selector/group/extensions/AddGroupArgTest.kt +++ b/src/test/kotlin/org/xpathqs/core/selector/group/extensions/AddGroupArgTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,7 @@ package org.xpathqs.core.selector.group.extensions import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.matchers.shouldBe import org.xpathqs.core.selector.args.ValueArg import org.xpathqs.core.selector.extensions.plus import org.xpathqs.core.selector.group.GroupSelector @@ -46,7 +47,7 @@ class AddGroupArgTest : AnnotationSpec() { ValueArg("position()=2") ).toXpath() }.THEN { - "//div[position()=2]" + "//div[position()=2]" shouldBe actual } } @@ -63,7 +64,7 @@ class AddGroupArgTest : AnnotationSpec() { ValueArg("position()=2") ).toXpath() }.THEN { - "(//div//div)[position()=2]" + "(//div//div)[position()=2]" shouldBe actual } } } \ No newline at end of file diff --git a/src/test/kotlin/org/xpathqs/core/selector/group/extensions/PrefixTest.kt b/src/test/kotlin/org/xpathqs/core/selector/group/extensions/PrefixTest.kt index 1fcaa14..dca9872 100644 --- a/src/test/kotlin/org/xpathqs/core/selector/group/extensions/PrefixTest.kt +++ b/src/test/kotlin/org/xpathqs/core/selector/group/extensions/PrefixTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,7 @@ package org.xpathqs.core.selector.group.extensions import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.matchers.shouldBe import org.xpathqs.core.selector.extensions.plus import org.xpathqs.core.selector.group.postfix import org.xpathqs.core.selector.group.prefix @@ -43,7 +44,7 @@ class PrefixTest : AnnotationSpec() { }.WHEN { given.prefix("*").toXpath() }.THEN { - "*div//div" + actual shouldBe "*div//div" } } @@ -58,12 +59,12 @@ class PrefixTest : AnnotationSpec() { }.WHEN { given.prefix("*").toXpath() }.THEN { - "//div//div" + actual shouldBe "//div//div" } } /** - * Check #3 require + * Check #1 require * @see org.xpathqs.core.selector.group.GroupSelector.postfix */ @Test @@ -73,7 +74,7 @@ class PrefixTest : AnnotationSpec() { }.WHEN { given.postfix("/*").toXpath() }.THEN { - "//div//div/*" + actual shouldBe "//div//div/*" } } } \ No newline at end of file From 9236064c94e9aa30890632ebbe8be1866542b4a1 Mon Sep 17 00:00:00 2001 From: Nikita Date: Sun, 1 Nov 2026 18:20:28 +0300 Subject: [PATCH 10/10] issue 193 --- detekt.yml | 2 +- .../core/reflection/ReflectionExtensions.kt | 21 +++++-- .../core/selector/base/CoreExtensions.kt | 59 ++++++++++++++++++- .../org/xpathqs/core/util/SelectorFactory.kt | 4 +- .../extensions/ReflectionExtensionsTest.kt | 4 +- 5 files changed, 78 insertions(+), 12 deletions(-) diff --git a/detekt.yml b/detekt.yml index cb0b3a6..d597b85 100644 --- a/detekt.yml +++ b/detekt.yml @@ -254,7 +254,7 @@ exceptions: active: true ignoreLabeled: false SwallowedException: - active: true + active: false ignoredExceptionTypes: - 'InterruptedException' - 'MalformedURLException' diff --git a/src/main/kotlin/org/xpathqs/core/reflection/ReflectionExtensions.kt b/src/main/kotlin/org/xpathqs/core/reflection/ReflectionExtensions.kt index 6ada55e..446a664 100644 --- a/src/main/kotlin/org/xpathqs/core/reflection/ReflectionExtensions.kt +++ b/src/main/kotlin/org/xpathqs/core/reflection/ReflectionExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,8 +30,11 @@ import org.xpathqs.core.selector.block.Block import org.xpathqs.core.selector.group.GroupSelector import kotlin.reflect.KClass import kotlin.reflect.KProperty +import kotlin.reflect.full.declaredMemberProperties import kotlin.reflect.full.superclasses +import kotlin.reflect.jvm.isAccessible import kotlin.reflect.jvm.javaField +import kotlin.reflect.jvm.javaGetter /** * Set of functional extensions to manipulate with
selectors
via Reflection @@ -212,15 +215,25 @@ internal fun Any.isInnerClass(): Boolean { return getInnerClassMember() != null } +fun KProperty<*>.isDeclaredInInnerClass(): Boolean { + val declaring = javaField?.declaringClass + ?: javaGetter?.declaringClass + ?: return false + if(declaring.name.endsWith("\$${declaring.simpleName}")) { + println() + } + return declaring.name.endsWith("\$${declaring.simpleName}") +} + /** * Return member of inner class */ internal fun Any.getInnerClassMember(): Any? { - val f = this::class.java.declaredFields.find { - it.name.contains("this$") + val f = this::class.declaredMemberProperties.firstOrNull{ + it.isDeclaredInInnerClass() } f?.isAccessible = true - return f?.get(this) + return f?.call(this) } /** diff --git a/src/main/kotlin/org/xpathqs/core/selector/base/CoreExtensions.kt b/src/main/kotlin/org/xpathqs/core/selector/base/CoreExtensions.kt index 8748430..4d65f55 100644 --- a/src/main/kotlin/org/xpathqs/core/selector/base/CoreExtensions.kt +++ b/src/main/kotlin/org/xpathqs/core/selector/base/CoreExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,7 +24,10 @@ package org.xpathqs.core.selector.base import org.xpathqs.core.reflection.* import org.xpathqs.core.selector.block.Block +//import org.xpathqs.core.util.SelectorFactory import java.lang.reflect.Constructor +import java.lang.reflect.Modifier +import kotlin.reflect.full.primaryConstructor /** * @return an ability of [Block] object to be cloned @@ -72,6 +75,18 @@ fun T.deepClone(): T { return newObj } +fun getOuterInstance(innerInstance: Any): Any? { + val innerClass = innerInstance.javaClass + return try { + val field = innerClass.getDeclaredField("this$0") + field.isAccessible = true + field.get(innerInstance) + } catch (e: NoSuchFieldException) { + // The field might have a different name or not exist. + null + } +} + /** * @return new instance of object class with the default properties * @@ -100,11 +115,49 @@ fun T.deepClone(): T { * Require #7 - ability to instantiate a [Block] class-object * @sample org.xpathqs.core.selector.base.extension.NewInstanceTest.r7_newInstanceForBlockObject */ + @Suppress("UNCHECKED_CAST") internal fun T.newInstance(): T { + + fun cloneSingleton(original: Any): Any { + val clazz = original::class.java + + // 1. Get the private no‑arg constructor + val constructor = clazz.getDeclaredConstructor() + constructor.isAccessible = true + + // 2. Create a new instance (this breaks the singleton) + val clone = constructor.newInstance() + + // 3. Copy all non‑static fields from the original to the clone + for (field in clazz.declaredFields) { + if (!Modifier.isStatic(field.modifiers)) { + field.isAccessible = true + field.set(clone, field.get(original)) + } + } + return clone + } + val res = if (this.isInnerClass()) { - val c = this.getConstructor(1) - c.newInstance(this.getInnerClassMember()) + if(this::class.isObject()) { + return cloneSingleton(this) as T + } + if(this::class.isInner) { + val outer = getOuterInstance(this) + val c = this::class.primaryConstructor + val p = c!!.parameters + c.callBy( + mapOf(p[0] to outer) + ) + } else { + if(this::class.primaryConstructor?.parameters?.size == 0) { + this::class.primaryConstructor!!.call() + } else { + val c = this.getConstructor(1) + c.newInstance(this.getInnerClassMember()) + } + } } else { val c = this.getConstructor(0) c.newInstance() diff --git a/src/main/kotlin/org/xpathqs/core/util/SelectorFactory.kt b/src/main/kotlin/org/xpathqs/core/util/SelectorFactory.kt index fb586e0..aa3615d 100644 --- a/src/main/kotlin/org/xpathqs/core/util/SelectorFactory.kt +++ b/src/main/kotlin/org/xpathqs/core/util/SelectorFactory.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 XPATH-QS + * Copyright (c) 2026 XPATH-QS * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,8 +32,6 @@ import org.xpathqs.core.selector.args.decorators.KVNormalizeSpaceDecorator import org.xpathqs.core.selector.base.ISelector import org.xpathqs.core.selector.compose.ComposeSelector import org.xpathqs.core.selector.compose.ComposeSelectorProps -import org.xpathqs.core.selector.extensions.addArg -import org.xpathqs.core.selector.extensions.addStringToArgs import org.xpathqs.core.selector.selector.Selector import org.xpathqs.core.selector.selector.SelectorProps import org.xpathqs.core.selector.xpath.XpathSelector diff --git a/src/test/kotlin/org/xpathqs/core/reflection/extensions/ReflectionExtensionsTest.kt b/src/test/kotlin/org/xpathqs/core/reflection/extensions/ReflectionExtensionsTest.kt index cfea933..c5809dd 100644 --- a/src/test/kotlin/org/xpathqs/core/reflection/extensions/ReflectionExtensionsTest.kt +++ b/src/test/kotlin/org/xpathqs/core/reflection/extensions/ReflectionExtensionsTest.kt @@ -29,7 +29,9 @@ import org.xpathqs.gwt.GIVEN class ReflectionExtensionsTest : AnnotationSpec() { class TestCls { - class InnerCls + class InnerCls { + val m1: String = "test1" + } val innerMember = InnerCls() class Class2 val member = Class2()