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 — Средне-продвинутый