Перейти к содержимому
Все вакансии

Senior QA Automation Java

ООО Аренадата Софтвер
Москваудалённая работаHH.ru

Описание вакансии

Что нужно делать: - Выполнять интеграционное, функциональное и end-2-end тестирование API - Фиксировать дефекты, непроработанные/спорные требования - Разрабатывать автотесты для имеющихся и новых продуктов компании в стэке Java - Разрабатывать вспомогательные общие решения - Проводить оптимизацию тестов - Проводить code review - Подготавливать и поддерживать тестовое окружение в Docker - Интегрировать тесты в CI в качестве Quality Gate, анализировать результаты выполнения - Тесно взаимодействовать с архитекторами, разработчиками, системными аналитиками в рамках продуктовой команды. Мы ждём, что вы: - Имеете опыт работы с Java более 5 лет, а также знаете Spring Boot 2+ - Обладаете уверенными знаниями ООП, принципов SOLID и структур данных - Имеете опыт работы с Maven - Обладаете опытом тестирования RESTful сервисов и Web UI - Имеете опыт написания интеграционных тестов - Имеете опыт тестирования распределенных систем - Умеете идентифицировать и устранять причины нестабильности автотестов - Имеете опыт работы с PostgreSQL - Имеете опыт работы в Unix средах, с системами контроля версий (Git) и с Docker контейнерами - Умеете проводить Code-review QA Automation Engineers (Java) - Способны решать сложные технические проблемы. Будет плюсом: - Знание классических алгоритмов программирования - Опыт реализации или поддержки CI пайплайнов - Опыт работы с распределенными хранилищами данных. Мы предлагаем Возможность вносить вклад в open source Выдаем мощное железо: ноутбук, до 2х мониторов Выстроенные процессы адаптация вертикальный (по грейдам) карьерный рост возможность горизонтальных карьерых переходов матрицы компетенций, планы развития Поддержка в профессиональном развитии внутреннее обучение по нашим продуктам внутренние технические конференции, оплата внешнего обучения и конференций Забота о Вашем здоровье ДМС со стоматологией с первого месяца работы страхование жизни оплата больничного 100% от зарплаты частичная компенсация онкострахования льготные условия ДМС для ваших близких Формат работы на выбор полностью удалённый из офиса в Москве гибридный No meeting day по четвергам *минимум встреч, максимум продуктивности Трудоустройство по ТК РФ ООО «АРЕНАДАТА СОФТВЕР» является владельцем бренда Arenadata window.jqueryMigrateLTROnLoad = function() {try { jQuery.UNSAFE_restoreLegacyHtmlPrefilter(); ; // // swiper ! function (e, t) { "object" == typeof exports && "undefined" != typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define(t) : (e = e || self).Swiper = t() }(this, function () { "use strict"; var f = "undefined" == typeof document ? { body: {}, addEventListener: function () {}, removeEventListener: function () {}, activeElement: { blur: function () {}, nodeName: "" }, querySelector: function () { return null }, querySelectorAll: function () { return [] }, getElementById: function () { return null }, createEvent: function () { return { initEvent: function () {} } }, createElement: function () { return { children: [], childNodes: [], style: {}, setAttribute: function () {}, getElementsByTagName: function () { return [] } } }, location: { hash: "" } } : document, J = "undefined" == typeof window ? { document: f, navigator: { userAgent: "" }, location: {}, history: {}, CustomEvent: function () { return this }, addEventListener: function () {}, removeEventListener: function () {}, getComputedStyle: function () { return { getPropertyValue: function () { return "" } } }, Image: function () {}, Date: function () {}, screen: {}, setTimeout: function () {}, clearTimeout: function () {} } : window, l = function (e) { for (var t = 0; t ")) { var o = "div"; for (0 === n.indexOf("- :~]/) ? (t || f).querySelectorAll(e .trim()) : [f.getElementById(e.trim().split("#")[1])], s = 0; s i.slides.length) break; s.push(i.slides.eq(r)[0]) } else s.push(i.slides.eq(i.activeIndex)[0]); for (t = 0; t = t.size) && ( t.visibleSlides.push(o), t.visibleSlidesIndexes.push(n), s.eq(n) .addClass(i.slideVisibleClass)) } o.progress = a ? -l : l } t.visibleSlides = E(t.visibleSlides) } }, updateProgress: function (e) { void 0 === e && (e = this && this.translate || 0); var t = this, i = t.params, s = t.maxTranslate() - t.minTranslate(), a = t.progress, r = t.isBeginning, n = t.isEnd, o = r, l = n; n = 0 == s ? r = !(a = 0) : (r = (a = (e - t.minTranslate()) / s) = a[u] && s = a[u] && s = a[u] && (p = u); n.normalizeSlideIndex && (p = r .length && (t = r.length - 1), p !== o) { var c = parseInt(i.slides.eq(p).attr("data-swiper-slide-index") || p, 10); ee.extend(i, { snapIndex: t, realIndex: c, previousIndex: o, activeIndex: p }), i.emit("activeIndexChange"), i.emit("snapIndexChange"), l !== c && i.emit( "realIndexChange"), (i.initialized || i.runCallbacksOnInit) && i.emit( "slideChange") } else t !== d && (i.snapIndex = t, i.emit("snapIndexChange")) }, updateClickedSlide: function (e) { var t = this, i = t.params, s = E(e.target).closest("." + i.slideClass)[0], a = !1; if (s) for (var r = 0; r = o.length && (c = o.length - 1), (p || n.initialSlide || 0) === (d || 0) && i && a .emit("beforeSlideChangeStart"); var h, v = -o[c]; if (a.updateProgress(v), n.normalizeSlideIndex) for (var f = 0; f = Math.floor(100 * l[ f]) && (r = f); if (a.initialized && r !== p) { if (!a.allowSlideNext && v a.translate && v > a.maxTranslate() && (p || 0) !== r) return !1 } return h = p t.slides.length - t .loopedSlides + a / 2 ? (t.loopFix(), r = s.children("." + i.slideClass + '[data-swiper-slide-index="' + e + '"]:not(.' + i.slideDuplicateClass + ")").eq(0).index(), ee.nextTick(function () { t.slideTo(r) })) : t.slideTo(r) : r > t.slides.length - a ? (t.loopFix(), r = s.children( "." + i.slideClass + '[data-swiper-slide-index="' + e + '"]:not(.' + i .slideDuplicateClass + ")").eq(0).index(), ee.nextTick(function () { t.slideTo(r) })) : t.slideTo(r) } else t.slideTo(r) } }; var v = { loopCreate: function () { var s = this, e = s.params, t = s.$wrapperEl; t.children("." + e.slideClass + "." + e.slideDuplicateClass).remove(); var a = t.children("." + e.slideClass); if (e.loopFillGroupWithBlank) { var i = e.slidesPerGroup - a.length % e.slidesPerGroup; if (i !== e.slidesPerGroup) { for (var r = 0; r a.length && (s.loopedSlides = a.length); var o = [], l = []; a.each(function (e, t) { var i = E(t); e = a.length - s .loopedSlides && o.push(t), i.attr("data-swiper-slide-index", e) }); for (var d = 0; d = a.length - r) { e = -a.length + s + r, e += r, t.slideTo(e, 0, !1, !0) && 0 != p && t.setTranslate(( d ? -t.translate : t.translate) - p) } t.allowSlidePrev = n, t.allowSlideNext = o }, loopDestroy: function () { var e = this.$wrapperEl, t = this.params, i = this.slides; e.children("." + t.slideClass + "." + t.slideDuplicateClass + ",." + t.slideClass + "." + t.slideBlankClass).remove(), i.removeAttr("data-swiper-slide-index") } }; var m = { appendSlide: function (e) { var t = this, i = t.$wrapperEl, s = t.params; if (s.loop && t.loopDestroy(), "object" == typeof e && "length" in e) for (var a = 0; a = J.screen.width - d)) { if (ee.extend(i, { isTouched: !0, isMoved: !1, allowTouchCallbacks: !0, isScrolling: void 0, startMoving: void 0 }), a.startX = n, a.startY = o, i.touchStartTime = ee.now(), t.allowClick = !0, t.updateSize(), t.swipeDirection = void 0, 0 a .startY && t.translate >= t.minTranslate()) return i .isTouched = !1, void(i.isMoved = !1) } else if (o a.startX && t.translate >= t.minTranslate()) return; if (i.isTouchEvent && f.activeElement && n.target === f .activeElement && E(n.target).is(i.formElements)) return i .isMoved = !0, void(t.allowClick = !1); if (i.allowTouchCallbacks && t.emit("touchMove", n), !(n .targetTouches && 1 s.touchAngle : 90 - u > s .touchAngle); if (i.isScrolling && t.emit("touchMoveOpposite", n), void 0 === i.startMoving && (a.currentX === a.startX && a.currentY === a.startY || (i.startMoving = !0)), i .isScrolling) i.isTouched = !1; else if (i.startMoving) { t.allowClick = !1, n.preventDefault(), s .touchMoveStopPropagation && !s.nested && n .stopPropagation(), i.isMoved || (s.loop && t .loopFix(), i.startTranslate = t.getTranslate(), t.setTransition(0), t.animating && t.$wrapperEl .trigger("webkitTransitionEnd transitionend"), i .allowMomentumBounce = !1, !s.grabCursor || ! 0 !== t.allowSlideNext && !0 !== t .allowSlidePrev || t.setGrabCursor(!0), t.emit( "sliderFirstMove", n)), t.emit("sliderMove", n), i.isMoved = !0; var c = t.isHorizontal() ? d : p; a.diff = c, c *= s.touchRatio, r && (c = -c), t .swipeDirection = 0 t.minTranslate() ? (h = !1, s .resistance && (i.currentTranslate = t .minTranslate() - 1 + Math.pow(-t .minTranslate() + i.startTranslate + c, v))) : c i.startTranslate && (i .currentTranslate = i.startTranslate), 0 s.threshold || i .allowThresholdMove)) return void(i .currentTranslate = i.startTranslate); if (!i.allowThresholdMove) return i .allowThresholdMove = !0, a.startX = a .currentX, a.startY = a.currentY, i .currentTranslate = i.startTranslate, void(a .diff = t.isHorizontal() ? a.currentX - a.startX : a.currentY - a.startY) } s.followFinger && ((s.freeMode || s .watchSlidesProgress || s .watchSlidesVisibility) && (t .updateActiveIndex(), t .updateSlidesClasses()), s.freeMode && ( 0 === i.velocities.length && i.velocities .push({ position: a[t.isHorizontal() ? "startX" : "startY"], time: i.touchStartTime }), i.velocities.push({ position: a[t.isHorizontal() ? "currentX" : "currentY"], time: ee.now() })), t.updateProgress(i.currentTranslate), t .setTranslate(i.currentTranslate)) } } } } } else i.startMoving && i.isScrolling && t.emit("touchMoveOpposite", n) }.bind(e), e.onTouchEnd = function (e) { var t = this, i = t.touchEventsData, s = t.params, a = t.touches, r = t.rtlTranslate, n = t.$wrapperEl, o = t.slidesGrid, l = t.snapGrid, d = e; if (d.originalEvent && (d = d.originalEvent), i.allowTouchCallbacks && t .emit("touchEnd", d), i.allowTouchCallbacks = !1, !i.isTouched) return i .isMoved && s.grabCursor && t.setGrabCursor(!1), i.isMoved = !1, void(i.startMoving = !1); s.grabCursor && i.isMoved && i.isTouched && (!0 === t.allowSlideNext || ! 0 === t.allowSlidePrev) && t.setGrabCursor(!1); var p, u = ee.now(), c = u - i.touchStartTime; if (t.allowClick && (t.updateClickedSlide(d), t.emit("tap", d), c -t.maxTranslate()) return void(t.slides.length t.minTranslate()) s.freeModeMomentumBounce ? (w - t .minTranslate() > y && (w = t.minTranslate() + y), T = t .minTranslate(), C = !0, i.allowMomentumBounce = !0) : w = t .minTranslate(), s.loop && s.centeredSlides && (x = !0); else if (s.freeModeSticky) { for (var S, E = 0; E -w) { S = E; break } w = -(w = Math.abs(l[S] - w) = s.longSwipesMs) && (t.updateProgress(), t .updateActiveIndex(), t.updateSlidesClasses()) } else { for (var M = 0, P = t.slidesSizesGrid[0], k = 0; k = o[k] && p = o[k] && (M = k, P = o[o.length - 1] - o[o.length - 2]); var L = (p - o[M]) / P; if (c > s.longSwipesMs) { if (!s.longSwipes) return void t.slideTo(t.activeIndex); "next" === t.swipeDirection && (L >= s.longSwipesRatio ? t.slideTo( M + s.slidesPerGroup) : t.slideTo(M)), "prev" === t .swipeDirection && (L > 1 - s.longSwipesRatio ? t.slideTo(M + s .slidesPerGroup) : t.slideTo(M)) } else { if (!s.shortSwipes) return void t.slideTo(t.activeIndex); "next" === t.swipeDirection && t.slideTo(M + s.slidesPerGroup), "prev" === t.swipeDirection && t.slideTo(M) } } }.bind(e), e.onClick = function (e) { this.allowClick || (this.params.preventClicks && e.preventDefault(), this .params.preventClicksPropagation && this.animating && (e .stopPropagation(), e.stopImmediatePropagation())) }.bind(e); var r = "container" === t.touchEventsTarget ? s : a, n = !!t.nested; if (te.touch || !te.pointerEvents && !te.prefixedPointerEvents) { if (te.touch) { var o = !("touchstart" !== i.start || !te.passiveListener || !t .passiveListeners) && { passive: !0, capture: !1 }; r.addEventListener(i.start, e.onTouchStart, o), r.addEventListener(i.move, e .onTouchMove, te.passiveListener ? { passive: !1, capture: n } : n), r.addEventListener(i.end, e.onTouchEnd, o) }(t.simulateTouch && !g.ios && !g.android || t.simulateTouch && !te.touch && g .ios) && (r.addEventListener("mousedown", e.onTouchStart, !1), f .addEventListener("mousemove", e.onTouchMove, n), f.addEventListener( "mouseup", e.onTouchEnd, !1)) } else r.addEventListener(i.start, e.onTouchStart, !1), f.addEventListener(i.move, e .onTouchMove, n), f.addEventListener(i.end, e.onTouchEnd, !1); (t.preventClicks || t.preventClicksPropagation) && r.addEventListener("click", e .onClick, !0), e.on(g.ios || g.android ? "resize orientationchange observerUpdate" : "resize observerUpdate", b, !0) }, detachEvents: function () { var e = this, t = e.params, i = e.touchEvents, s = e.el, a = e.wrapperEl, r = "container" === t.touchEventsTarget ? s : a, n = !!t.nested; if (te.touch || !te.pointerEvents && !te.prefixedPointerEvents) { if (te.touch) { var o = !("onTouchStart" !== i.start || !te.passiveListener || !t .passiveListeners) && { passive: !0, capture: !1 }; r.removeEventListener(i.start, e.onTouchStart, o), r.removeEventListener(i .move, e.onTouchMove, n), r.removeEventListener(i.end, e.onTouchEnd, o) }(t.simulateTouch && !g.ios && !g.android || t.simulateTouch && !te.touch && g .ios) && (r.removeEventListener("mousedown", e.onTouchStart, !1), f .removeEventListener("mousemove", e.onTouchMove, n), f.removeEventListener( "mouseup", e.onTouchEnd, !1)) } else r.removeEventListener(i.start, e.onTouchStart, !1), f.removeEventListener(i .move, e.onTouchMove, n), f.removeEventListener(i.end, e.onTouchEnd, !1); (t.preventClicks || t.preventClicksPropagation) && r.removeEventListener("click", e .onClick, !0), e.off(g.ios || g.android ? "resize orientationchange observerUpdate" : "resize observerUpdate", b) } }, breakpoints: { setBreakpoint: function () { var e = this, t = e.activeIndex, i = e.initialized, s = e.loopedSlides; void 0 === s && (s = 0); var a = e.params, r = a.breakpoints; if (r && (!r || 0 !== Object.keys(r).length)) { var n = e.getBreakpoint(r); if (n && e.currentBreakpoint !== n) { var o = n in r ? r[n] : void 0; o && ["slidesPerView", "spaceBetween", "slidesPerGroup"].forEach(function ( e) { var t = o[e]; void 0 !== t && (o[e] = "slidesPerView" !== e || "AUTO" !== t && "auto" !== t ? "slidesPerView" === e ? parseFloat(t) : parseInt(t, 10) : "auto") }); var l = o || e.originalParams, d = l.direction && l.direction !== a.direction, p = a.loop && (l.slidesPerView !== a.slidesPerView || d); d && i && e.changeDirection(), ee.extend(e.params, l), ee.extend(e, { allowTouchMove: e.params.allowTouchMove, allowSlideNext: e.params.allowSlideNext, allowSlidePrev: e.params.allowSlidePrev }), e.currentBreakpoint = n, p && i && (e.loopDestroy(), e.loopCreate(), e.updateSlides(), e.slideTo(t - s + e.loopedSlides, 0, !1)), e.emit( "breakpoint", l) } } }, getBreakpoint: function (e) { if (e) { var t = !1, i = []; Object.keys(e).forEach(function (e) { i.push(e) }), i.sort(function (e, t) { return parseInt(e, 10) - parseInt(t, 10) }); for (var s = 0; s = J .innerWidth && !t && (t = a) } return t || "max" } } }, checkOverflow: { checkOverflow: function () { var e = this, t = e.isLocked; e.isLocked = 1 === e.snapGrid.length, e.allowSlideNext = !e.isLocked, e .allowSlidePrev = !e.isLocked, t !== e.isLocked && e.emit(e.isLocked ? "lock" : "unlock"), t && t !== e.isLocked && (e.isEnd = !1, e.navigation.update()) } }, classes: { addClasses: function () { var t = this.classNames, i = this.params, e = this.rtl, s = this.$el, a = []; a.push("initialized"), a.push(i.direction), i.freeMode && a.push("free-mode"), te .flexbox || a.push("no-flexbox"), i.autoHeight && a.push("autoheight"), e && a .push("rtl"), 1 Math.abs(n.pixelY))) return !0; a = n.pixelX * r } else { if (!(Math.abs(n.pixelY) > Math.abs(n.pixelX))) return !0; a = n.pixelY } else a = Math.abs(n.pixelX) > Math.abs(n.pixelY) ? -n.pixelX * r : -n.pixelY; if (0 === a) return !0; if (s.invert && (a = -a), i.params.freeMode) { i.params.loop && i.loopFix(); var o = i.getTranslate() + a * s.sensitivity, l = i.isBeginning, d = i.isEnd; if (o >= i.minTranslate() && (o = i.minTranslate()), o i - 1 - 2 * e.loopedSlides && (r -= i - 2 * e .loopedSlides), n - 1 a .dynamicMainBullets - 1 ? e.pagination.dynamicBulletIndex = a .dynamicMainBullets - 1 : e.pagination.dynamicBulletIndex "; s.html(a), e.pagination.bullets = s.find("." + t.bulletClass) } "fraction" === t.type && (a = t.renderFraction ? t.renderFraction.call(e, t .currentClass, t.totalClass) : ' / ', s.html(a)), "progressbar" === t.type && (a = t.renderProgressbar ? t.renderProgressbar.call( e, t.progressbarFillClass) : '', s.html(a)), "custom" !== t.type && e.emit("paginationRender", e .pagination.$el[0]) } }, init: function () { var i = this, e = i.params.pagination; if (e.el) { var t = E(e.el); 0 !== t.length && (i.params.uniqueNavElements && "string" == typeof e.el && 1 '), a.append(r)), ee.extend(t, { $el: a, el: a[0], $dragEl: r, dragEl: r[0] }), s.draggable && t.enableDraggable() } }, destroy: function () { this.scrollbar.disableDraggable() } }, A = { run: function () { var e = this, t = e.slides.eq(e.activeIndex), i = e.params.autoplay.delay; t.attr("data-swiper-autoplay") && (i = t.attr("data-swiper-autoplay") || e.params .autoplay.delay), clearTimeout(e.autoplay.timeout), e.autoplay.timeout = ee .nextTick(function () { e.params.autoplay.reverseDirection ? e.params.loop ? (e.loopFix(), e .slidePrev(e.params.speed, !0, !0), e.emit("autoplay")) : e .isBeginning ? e.params.autoplay.stopOnLastSlide ? e.autoplay.stop() : ( e.slideTo(e.slides.length - 1, e.params.speed, !0, !0), e.emit( "autoplay")) : (e.slidePrev(e.params.speed, !0, !0), e.emit( "autoplay")) : e.params.loop ? (e.loopFix(), e.slideNext(e.params .speed, !0, !0), e.emit("autoplay")) : e.isEnd ? e.params.autoplay .stopOnLastSlide ? e.autoplay.stop() : (e.slideTo(0, e.params.speed, !0, !0), e.emit("autoplay")) : (e.slideNext(e.params.speed, !0, !0), e .emit("autoplay")) }, i) }, start: function () { var e = this; return void 0 === e.autoplay.timeout && (!e.autoplay.running && (e.autoplay.running = ! 0, e.emit("autoplayStart"), e.autoplay.run(), !0)) }, stop: function () { var e = this; return !!e.autoplay.running && (void 0 !== e.autoplay.timeout && (e.autoplay.timeout && (clearTimeout(e.autoplay.timeout), e.autoplay.timeout = void 0), e.autoplay .running = !1, e.emit("autoplayStop"), !0)) }, pause: function (e) { var t = this; t.autoplay.running && (t.autoplay.paused || (t.autoplay.timeout && clearTimeout(t .autoplay.timeout), t.autoplay.paused = !0, 0 !== e && t.params.autoplay .waitForTransition ? (t.$wrapperEl[0].addEventListener("transitionend", t .autoplay.onTransitionEnd), t.$wrapperEl[0].addEventListener( "webkitTransitionEnd", t.autoplay.onTransitionEnd)) : (t.autoplay .paused = !1, t.autoplay.run()))) } }, $ = { setTranslate: function () { for (var e = this, t = e.slides, i = 0; i '), v.append(y)), 0 === S.length && (S = E( ''), v.append(S)), y.length && (y[0].style.opacity = 0 { const videos = document.querySelectorAll('.tmpl_hh_video'); videos.forEach((video) => { const content = video.querySelector('.tmpl_hh_video__content'); const id = video.dataset.id; content.addEventListener('click', () => { video.innerHTML = ''; const iframe = video.querySelector('iframe'); if (iframe !== null) { iframe.addEventListener('load', function () { setTimeout(() => { iframe.contentWindow.postMessage(JSON.stringify({ type: 'player:play', data: {} }), '*'); }, 300); }); } }); }); } function DOM_HH_Ready() { initSliders(); initVideoPreview(); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", DOM_HH_Ready); } else { DOM_HH_Ready(); } // } catch (e) {console.error(e);}}; Ключевые навыки - Java - Spring Framework - REST API - Linux - Docker - PostgreSQL - Английский — B2 — Средне-продвинутый
Открыть оригинал на HH.ru